<template>
  <div class="map-container" id="map-container" ref="map-container">
    <div id="allmap"></div>

    <div class="drawing-tools">
      <el-dropdown class="drawing-tools-dropdown" :class="{'is-active': toolCommond === 'fenceAlarm'}"
        @command="handleCommand" placement="bottom-start">
        <div class="drawing-tools-item">
          <img src="~@/assets/images/bmap/alarm.png" />
          <span style="color:#e0620d">告警</span>
        </div>
        <el-dropdown-menu class="drawing-tools-dropdown-menu" slot="dropdown">
          <el-dropdown-item command="fenceAlarm">电子围栏</el-dropdown-item>
          <el-dropdown-item v-if="hasWarningModule" command="warningAlarm">区域告警</el-dropdown-item>
          <el-dropdown-item v-if="hasSuppliesModule" command="suppliesAlarm">物资告警</el-dropdown-item>
          <!-- <el-dropdown-item command="eventAlarm">事件告警</el-dropdown-item> -->
        </el-dropdown-menu>
      </el-dropdown>
      <div class="drawing-tools-item" :class="{'is-active': toolCommond === 'hander'}" @click="handleCommand('hander')">
        <img src="~@/assets/images/bmap/move.png" />
        <span>移动</span>
      </div>
      <el-dropdown class="drawing-tools-dropdown"
        :class="{'is-active': toolCommond === 'circle' || toolCommond === 'rectangle' || toolCommond === 'polygon'}"
        @command="handleCommand" placement="bottom-start">
        <div class="drawing-tools-item">
          <img src="~@/assets/images/bmap/encircled.png" />
          <span>圈选</span>
        </div>
        <el-dropdown-menu class="drawing-tools-dropdown-menu" slot="dropdown">
          <el-dropdown-item command="circle">圆形</el-dropdown-item>
          <el-dropdown-item command="rectangle">矩形</el-dropdown-item>
          <el-dropdown-item command="polygon">多边形</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
      <el-dropdown class="drawing-tools-dropdown"
        :class="{'is-active': toolCommond === 'fence' || toolCommond === 'distance' || toolCommond === 'area'}"
        @command="handleCommand" placement="bottom-start">
        <div class="drawing-tools-item">
          <img src="~@/assets/images/bmap/tool.png" />
          <span>工具</span>
        </div>
        <el-dropdown-menu class="drawing-tools-dropdown-menu" slot="dropdown">
          <el-dropdown-item command="fence">电子围栏</el-dropdown-item>
          <el-dropdown-item command="distance">测距离</el-dropdown-item>
          <el-dropdown-item command="circle_area">圆形面积</el-dropdown-item>
          <el-dropdown-item command="rectangle_area">矩形面积</el-dropdown-item>
          <el-dropdown-item command="polygon_area">多边面积</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>

      <div class="drawing-tools-item" @click="handleCommand('center')">
        <img src="~@/assets/images/bmap/defualt.png" />
        <span>默认</span>
      </div>
      <div class="drawing-tools-item" @click="handleCommand('refresh')">
        <img src="~@/assets/images/bmap/refresh.png" />
        <span>刷新</span>
      </div>
      <div class="drawing-tools-item last" @click="handleCommand('fullscreen')">
        <img src="~@/assets/images/bmap/fullscreen.png" />
        <span>全屏</span>
      </div>
    </div>

    <div class="overlay-tools">
      <el-checkbox-group v-model="showOverlayTypes" border size="small">
        <el-checkbox-button :class="{'is-hidden': type.value === 'warning' && !hasWarningModule}"
          v-for="type in overlayTypes" :label="type.value" :key="type.value">
          <svg-icon :icon-class="`overlay-${type.value}`"></svg-icon>
          <span>{{type.label}}</span>
        </el-checkbox-button>
      </el-checkbox-group>
    </div>

    <div class="left-panel">
      <div class="nav">
        <div class="nav-btn list" @click="handleList">
          <i class="el-icon-s-operation"></i>
        </div>
        <el-input class="searchbox-text" size="mini" placeholder="地图搜索" clearable v-model="searchKey"
          @change="handleQuery" @keyup.enter.native="handleQuery" />
        <div class="nav-btn searchbox" @click="handleQuery">
          <i class="el-icon-search"></i>
        </div>
      </div>

      <el-row class="content" v-show="showContent">
        <div class="divider"></div>
        <el-col class="content-tabs" v-show="showTree">
          <div class="result-content" v-show="showResult">
            <el-button type="text" class="back-btn" icon="el-icon-arrow-left" @click="handleClear">返回</el-button>
            <div class="right">
              <span class="label">结果：</span>
              <svg-icon icon-class="gis-user" />
              <span class="total">{{ userResultCount }}</span>
              <svg-icon icon-class="gis-camera" />
              <span class="total">{{ videoResultCount }}</span>
              <svg-icon icon-class="gis-supplies" />
              <span class="total">{{ suppliesResultCount }}</span>
            </div>
          </div>
          <el-tabs tab-position="left" v-model="activeMenu" type="border-card">
            <el-tab-pane name="contact">
              <div class="tab-pane-item" slot="label">
                <svg-icon icon-class="contacts" />
                <span>通讯录</span>
              </div>
              <el-scrollbar class="default-scrollbar" wrap-class="default-scrollbar__wrap">
                <el-tree :data="userTreeData" :props="userProps" @node-click="data => nodeClick(data, 'user')"
                  @check="clickCheckbox" @check-change="(data, checked) => checkChange(data, checked, 'user')"
                  highlight-current show-checkbox accordion node-key="nid" :filter-node-method="handleUserFilter"
                  ref="userTree" :indent="getIndent()">
                  <span class="custom-tree-node" slot-scope="{ node, data }">
                    <span class="custom-node-label" :class="{ 'parent-span': data.children }">{{ data.label }}</span>
                    <span class="custom-node-mark"
                      v-show="data.type !== 'group' && (!data.obj.pos_x && !data.obj.pos_y)">
                      <el-button type="text" class="mark-button" size="mini" @click.stop="() => handleMark(data, node)">
                        <u>标注</u>
                      </el-button>
                    </span>
                  </span>
                </el-tree>
              </el-scrollbar>
              <div class="tabs-footer">
                <div class="tools-btn">
                  <el-tooltip content="语音" effect="light" placement="top" v-show="audioBtnVisiable">
                    <img src="~@/assets/images/VoiceCall.png" @click.stop="handleMakeCall('audio')" />
                  </el-tooltip>
                  <el-tooltip content="视频" effect="light" placement="top" v-show="videoBtnVisiable">
                    <img src="~@/assets/images/VideoCall.png" @click.stop="handleMakeCall('video')" />
                  </el-tooltip>
                  <el-tooltip content="监控" effect="light" placement="top" v-show="cameraBtnVisiable">
                    <img src="~@/assets/images/Video2.png" @click.stop="handleViewCamara()" />
                  </el-tooltip>
                  <el-tooltip content="会议" effect="light" placement="top" v-show="callBtnVisiable">
                    <img src="~@/assets/images/Meeting.png" @click.stop="handleMeeting()" />
                  </el-tooltip>
                  <el-tooltip content="短信" effect="light" placement="top" v-show="smsBtnVisiable">
                    <img src="~@/assets/images/Msg.png" @click.stop="handleSms()" />
                  </el-tooltip>
                  <el-tooltip content="广播" effect="light" placement="top" v-show="callBtnVisiable">
                    <img src="~@/assets/images/Broadcast2.png" @click.stop="handleBroadCast()" />
                  </el-tooltip>
                  <el-tooltip content="对讲编组" effect="light" placement="top" v-show="intercomBtnVisiable">
                    <img src="~@/assets/images/AllowSpeaks.png" @click.stop="handleIntercom()" />
                  </el-tooltip>
                </div>
              </div>
            </el-tab-pane>
            <el-tab-pane name="video">
              <div class="tab-pane-item" slot="label">
                <svg-icon icon-class="video" />
                <span>视频</span>
              </div>
              <el-scrollbar class="default-scrollbar" wrap-class="default-scrollbar__wrap">
                <el-tree :data="videoTreeData" :props="videoProps" @node-click="data => nodeClick(data, 'video')"
                  @check="clickCheckboxForVideo" @check-change="(data, checked) => checkChange(data, checked, 'video')"
                  highlight-current show-checkbox accordion node-key="nid" :filter-node-method="handleVideoFilter"
                  ref="videoTree" :indent="getIndent()">
                  <span class="custom-tree-node" slot-scope="{ node, data }">
                    <span class="custom-node-label" :class="{ 'parent-span': data.children }">{{ data.label }}</span>
                    <span v-show="data.type !== 'videogroup' && (!data.obj.pos_x && !data.obj.pos_y)">
                      <el-button type="text" class="mark-button" size="mini" @click.stop="() => handleMark(data, node)">
                        <u>标注</u>
                      </el-button>
                    </span>
                  </span>
                </el-tree>
              </el-scrollbar>
              <div class="tabs-footer">
                <div class="tools-btn">
                  <el-tooltip content="查视频" effect="light" placement="top" v-show="playVideoBtnVisiable">
                    <img src="~@/assets/images/VideoCall.png" @click.stop="handlePlayVideo" />
                  </el-tooltip>
                </div>
              </div>
            </el-tab-pane>
            <el-tab-pane name="supplies" v-if="hasSuppliesModule">
              <div class="tab-pane-item" slot="label">
                <svg-icon icon-class="supplies" />
                <span>物资</span>
              </div>
              <el-scrollbar class="default-scrollbar" wrap-class="default-scrollbar__wrap">
                <el-tree :data="suppliesTreeData" :props="suppliesProps"
                  @node-click="data => nodeClick(data, 'supplies')"
                  @check-change="(data, checked) => checkChange(data, checked, 'supplies')" highlight-current
                  :indent="getIndent()" show-checkbox accordion node-key="nid"
                  :filter-node-method="handleSuppliesFilter" ref="suppliesTree">
                  <span class="custom-tree-node" slot-scope="{ node, data }">
                    <span class="custom-node-label" :class="{ 'parent-span': data.children }">{{ data.label }}</span>
                    <span v-show="data.type !== 'suppliesgroup' && (!data.obj.pos_x && !data.obj.pos_y)">
                      <el-button type="text" class="mark-button" size="mini" @click.stop="handleMark(data, node)">
                        <u>标注</u>
                      </el-button>
                    </span>
                  </span>
                </el-tree>
              </el-scrollbar>
              <div class="tabs-footer"></div>
            </el-tab-pane>
            <el-tab-pane name="event" v-if="hasEventModule">
              <div class="tab-pane-item" slot="label">
                <svg-icon icon-class="emergency" />
                <span>事件</span>
              </div>
              <el-scrollbar class="default-scrollbar" wrap-class="default-scrollbar__wrap">
                <el-tree :data="eventTreeData" :props="eventProps" @node-click="data => nodeClick(data, 'event')"
                  @check-change="(data, checked) => checkChange(data, checked, 'event')" show-checkbox highlight-current
                  :indent="getIndent()" accordion node-key="nid" :filter-node-method="handleEventFilter"
                  ref="eventTree">
                  <span class="custom-tree-node" slot-scope="{ node, data }">
                    <span class="custom-node-label"
                      :class="{ 'parent-span': data.type ==='eventgroup' }">{{ data.label }}</span>
                    <span v-show="data.type !== 'eventgroup' && (!data.obj.pos_x && !data.obj.pos_y)">
                      <el-button type="text" class="mark-button" size="mini" @click.stop="handleMark(data, node)">
                        <u>标注</u>
                      </el-button>
                    </span>
                  </span>
                </el-tree>
              </el-scrollbar>

              <div class="tabs-footer"></div>
            </el-tab-pane>
            <el-tab-pane name="warning" v-if="hasWarningModule">
              <div class="tab-pane-item" slot="label">
                <svg-icon icon-class="warning" />
                <span>告警</span>
              </div>
              <el-scrollbar class="default-scrollbar" wrap-class="default-scrollbar__wrap">
                <el-tree :data="warningTreeData" :props="warningProps" @node-click="data => nodeClick(data, 'warning')"
                  @check-change="(data, checked) => checkChange(data, checked, 'warning')" show-checkbox
                  highlight-current :indent="getIndent()" accordion node-key="nid"
                  :filter-node-method="handleWarningFilter" ref="warningTree">
                  <span class="custom-tree-node" slot-scope="{ node, data }">
                    <span class="custom-node-label"
                      :class="{ 'parent-span': data.type !=='warning' }">{{ data.label }}</span>
                    <!-- <span v-show="data.type === 'warning'">
                      <el-button type="text" class="mark-button" size="mini" @click.stop="handleMark(data, node)">
                        <u>标注</u>
                      </el-button>
                    </span>-->
                  </span>
                </el-tree>
              </el-scrollbar>

              <div class="tabs-footer">
                <div class="tools-btn">
                  <el-tooltip content="告警记录" effect="light" placement="top">
                    <img src="~@/assets/images/warning_history.png" @click.stop="handleWarning()" />
                  </el-tooltip>
                </div>
              </div>
            </el-tab-pane>
          </el-tabs>
        </el-col>
        <el-col class="content-location" v-show="showTrack">
          <div class="result-content">
            <el-button type="text" class="back-btn" icon="el-icon-arrow-left" @click="handleTrackClear">返回</el-button>
            <div class="right">
              <span class="label">轨迹查询结果：</span>
              <svg-icon icon-class="location" />
              <span class="total">{{ trackTotal }}</span>
            </div>
          </div>
          <div class="location-query">
            <el-date-picker size="mini" v-model="trackForm.daterange" :clearable="false" type="daterange"
              :picker-options="pickerOptions" @change="daterangeChange" range-separator="-" start-placeholder="开始日期"
              end-placeholder="结束日期" popper-class="custom-daterange-popper"></el-date-picker>

            <el-button size="mini" class="query-btn" @click="queryTrack">查询</el-button>
          </div>
          <el-table :data="trackData" ref="trackTable" height="calc(100% - 8.7vw)" width="100%" size="small" stripe
            cell-class-name="table-cell-custom" header-cell-class-name="table-herder-custom" highlight-current-row
            @current-change="trackPointClick">
            <el-table-column prop="X" show-overflow-tooltip align="center" label="经度"></el-table-column>
            <el-table-column prop="Y" show-overflow-tooltip align="center" label="纬度"></el-table-column>
            <el-table-column prop="SJ" align="center"
              :formatter="(row, column, cellValue, index) => formatterTime(cellValue)" label="时间" width="130">
            </el-table-column>
          </el-table>

          <pagination v-show="trackTotal > 0" layout="prev, pager, next" :small="true" :total="trackTotal"
            :page.sync="trackForm.pageNum" :limit.sync="trackForm.pageSize" @pagination="queryCacheTrack" />
        </el-col>
      </el-row>
    </div>

    <!-- webrtc视频 -->
    <div class="webrtc-video" v-show="showWebrtcVideo" id="webrtc-video"
      @touchstart="touchstart($event, 'webrtc-video')" @touchmove="touchmove($event, 'webrtc-video')" v-drag>
      <div class="video_remote_div">
        <video-box ref="webrtcVideoBox" :dispenseable="true" videoIndex="_remote"
          @videoDistribution="openVideoDistribution" :webrtc2Sip.sync="webrtc2Sip" @videoExit="videoExit" size="medium">
        </video-box>
        <div class="video_local_div">
          <video id="video_local" class="video_local" autoplay></video>
        </div>
      </div>
    </div>

    <!-- 摄像头视频 -->
    <camara-box v-if="showCamaraBox" ref="camaraBox" :open.sync="showCamaraBox" :formData="camaraBoxData"
      :playingData="camaraBoxPlayingData" @makeCall="makeCall" @videoDistribution="openVideoDistribution"></camara-box>

    <!-- 会议预览窗口 -->
    <el-dialog title="会议总览" center modal custom-class="meeting-preview" :visible.sync="openPreview"
      :modal-append-to-body="false" :close-on-click-modal="false">
      <div class="preview-box">
        <div class="preview-item" v-for="item in meetingList" :key="item.meetID" @click.stop="currentMeeting = item"
          :class="{
            isCurrent: currentMeeting && item.meetID === currentMeeting.meetID
          }">
          <div class="meeting-label">
            <img src="@/assets/images/allowspeak.png" />
            <span>{{ item.meetName }}</span>
          </div>
          <div class="meeting-info">
            <div>
              <span class="label">会议创建者：</span>
              <span class="value">{{ operatorInfo ? operatorInfo.realName : '' }}</span>
            </div>
            <div>
              <span class="label">会议人数：</span>
              <span class="value">{{ item.memberSize }}</span>
            </div>
          </div>
        </div>
      </div>
      <div slot="footer" class="preview-footer">
        <el-button @click="handleCreateMeet">新 建</el-button>
        <el-button @click="joinMeet">确 定</el-button>
      </div>
      <!-- 创建会议 -->
      <el-dialog title="创建会议" :modal="false" center :visible.sync="openCreateMeet" custom-class="preview-inner-dialog">
        <svg-icon class="back-to-list" icon-class="back" @click="openCreateMeet = false"></svg-icon>
        <el-form :model="meetForm" :rules="rules" ref="meetForm" hide-required-asterisk>
          <el-form-item label="会议名称：" prop="meetName">
            <el-input v-model="meetForm.meetName" placeholder="请输入会议名称" />
          </el-form-item>
          <el-form-item label="默认发言：" prop="isAllowSpeak">
            <el-switch v-model="meetForm.isAllowSpeak" active-color="#13ce66" inactive-color="#ff4949" active-text="开"
              inactive-text="关" active-value="yes" inactive-value="no"></el-switch>
          </el-form-item>
          <el-form-item label="永许呼入：" prop="callinState">
            <el-switch v-model="meetForm.callinState" active-color="#13ce66" inactive-color="#ff4949" active-text="开"
              inactive-text="关" active-value="1" inactive-value="0"></el-switch>
          </el-form-item>
          <el-form-item label="会议号码：" prop="callinNum" v-if="meetForm.callinState === '1'">
            <el-input v-model="meetForm.callinNum" placeholder="请输入会议号码" />
          </el-form-item>
          <el-form-item label="会议密码：" prop="callinPwd" v-if="meetForm.callinState === '1'">
            <el-input v-model="meetForm.callinPwd" placeholder="请输入会议密码" />
          </el-form-item>
        </el-form>
        <div slot="footer" class="preview-inner-footer">
          <el-button @click="createMeet">确 定</el-button>
          <el-button @click="openCreateMeet = false">取 消</el-button>
        </div>
      </el-dialog>
    </el-dialog>

    <!-- 视频分发窗口 -->
    <distribution v-if="openDistribution" :open.sync="openDistribution" :source="distributionSource"></distribution>

    <!-- 物资表单 -->
    <supplies-form v-if="openSuppliesForm" :title="suppliesFormTitle" :open.sync="openSuppliesForm"
      @update="updateSupplies" :formData="suppliesForm"></supplies-form>
    <!-- 摄像头窗口 -->
    <camera-form v-if="openCameraForm" :title="cameraFormTitle" :open.sync="openCameraForm" :formData="cameraForm">
    </camera-form>
    <!-- 职员窗口 -->
    <employee-form v-if="openEmployeeForm" :title="employeeFormTitle" :open.sync="openEmployeeForm"
      :formData="employeeForm"></employee-form>
    <!-- 操作员窗口 -->
    <operator-form v-if="openOperatorForm" :title="operatorFormTitle" :open.sync="openOperatorForm"
      :formData="operatorForm"></operator-form>
    <!-- 事件窗口 -->
    <event-form v-if="openEventForm" :title="eventFormTitle" :open.sync="openEventForm" :formData="eventForm">
    </event-form>
    <!-- 物资告警信息窗口 -->
    <warn-dialog v-if="openWarnDialog" :open.sync="openWarnDialog"></warn-dialog>

    <!-- 告警信息窗口 -->
    <warning-dialog v-if="openWarning" :open.sync="openWarning" :formData="warningFormData"></warning-dialog>

    <!-- 对讲编组 -->
    <el-dialog title="对讲编组" :modal="false" v-if="openIntercom" custom-class="intercom-dialog"
      :visible.sync="openIntercom">
      <div class="intercom-body">
        <el-radio-group v-model="intercomForm.callingMode" class="outer-radio-group">
          <el-radio label="once">单次编组</el-radio>
          <el-divider></el-divider>
          <el-radio label="temporary">
            <span>临时编组</span>
            <span v-show="intercomForm.callingMode === 'temporary'" class="temporary-span">
              <el-input-number v-model="intercomForm.Time" :min="1" :max="24" size="small" placeholder="输入时间">
              </el-input-number>
              <el-radio-group v-model="intercomForm.Time" class="inner-radio-group">
                <el-radio-button :label="1" :class="{ 'is-active': intercomForm.Time === 1 }">1小时</el-radio-button>
                <el-radio-button :label="2" :class="{ 'is-active': intercomForm.Time === 2 }">2小时</el-radio-button>
                <el-radio-button :label="8" :class="{ 'is-active': intercomForm.Time === 8 }">8小时</el-radio-button>
                <el-radio-button :label="24" :class="{ 'is-active': intercomForm.Time === 24 }">24小时</el-radio-button>
              </el-radio-group>
            </span>
          </el-radio>
          <el-divider></el-divider>
          <el-radio label="perpetual">永久编组</el-radio>
        </el-radio-group>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="selectCallEx">确 定</el-button>
      </div>
    </el-dialog>

    <!-- 短信窗口 -->
    <sms-form v-if="openSms" :open.sync="openSms" :formData="smsFormData"></sms-form>

    <!-- 广播窗口 -->
    <broadcast-form v-if="openBroadcast" :open.sync="openBroadcast" :formData="broadcastFormData"></broadcast-form>
    <!-- 选择围栏用户窗口 -->
    <fence-user v-if="openFenceUser" :open.sync="openFenceUser" :formData="fenceUserFormData" @save="fenceUserSave">
    </fence-user>
    <!-- 围栏报警历史记录窗口 -->
    <fence-alarm v-if="openFenceAlarm" :open.sync="openFenceAlarm" :formData="fenceAlarmFormData"></fence-alarm>
  </div>
</template>

<script>
import { mapGetters, mapMutations } from 'vuex'
import { msgError, msgSuccess, msgWarning } from '@/utils/message'
import CustomMarker from '@/utils/CustomMarker'
import CustomTextIconOverlay from '@/utils/CustomTextIconOverlay'
import CustomFence from '@/utils/CustomFence'
import CustomInfoBox from '@/utils/CustomInfoBox'
import {
  parseStr2Date,
  parseStr2DateStr,
  parseTime2Str,
  selectDictLabel,
} from '@/utils/microsys'
import Webrtc2Sip from '@/utils/Webrtc2Sip'
import screenfull from 'screenfull'
import playRtsp from '@/utils/webrtc2rtsp'
import deepClone from '@/utils/clone'

import { modifySupplies, getSupplies } from '@/api/supplies'
import { putObj, getEvent } from '@/api/event'

// 组件
import Pagination from '@/components/Pagination'
import VideoBox from '@/components/Meeting/VideoBox'
import Distribution from '@/components/Meeting/Distribution'
import SuppliesForm from './SuppliesForm'
import CameraForm from './CameraForm'
import EmployeeForm from './EmployeeForm'
import OperatorForm from './OperatorForm'
import EventForm from './EventForm'
import WarnDialog from './WarnDialog'
import WarningDialog from './WarningDialog'
import SmsForm from './SmsForm'
import BroadcastForm from './BroadcastForm'
import CamaraBox from './CamaraBox'
import FenceUser from './FenceUser'
import FenceAlarm from './FenceAlarm'

export default {
  name: 'Map',
  components: {
    Pagination,
    VideoBox,
    Pagination,
    Distribution,
    SuppliesForm,
    CameraForm,
    WarnDialog,
    WarningDialog,
    EmployeeForm,
    OperatorForm,
    EventForm,
    SmsForm,
    BroadcastForm,
    CamaraBox,
    FenceUser,
    FenceAlarm,
  },

  data() {
    return {
      toolCommond: '', // 地图工具栏选中
      // 时间选择器选项
      pickerOptions: {
        disabledDate(time) {
          return time.getTime() > Date.now()
        },
        onPick({ maxDate, minDate }) {
          if (
            maxDate &&
            minDate &&
            maxDate.getTime() - minDate.getTime() > 24 * 3600 * 1000
          ) {
            msgWarning('最多只能查询24小时轨迹')
          }
        },
      },
      userProps: {
        disabled: (data, node) => {
          node.isLeaf = data.isLeaf
          return data.type !== 'group' && (!data.obj.pos_x || !data.obj.pos_y)
        },
      },
      videoProps: {
        disabled: (data, node) => {
          node.isLeaf = data.isLeaf
          return data.type === 'video' && (!data.obj.pos_x || !data.obj.pos_y)
        },
      },
      suppliesProps: {
        children: 'children',
        disabled: (data, node) => {
          node.isLeaf = data.isLeaf
          return (
            data.type !== 'suppliesgroup' &&
            (!data.obj.pos_x || !data.obj.pos_y)
          )
        },
      },
      eventProps: {
        children: 'children',
        disabled: (data, node) => {
          node.isLeaf = data.isLeaf
          return (
            data.type !== 'eventgroup' && (!data.obj.pos_x || !data.obj.pos_y)
          )
        },
      },
      warningProps: {
        children: 'children',
        disabled: (data, node) => {
          node.isLeaf = data.isLeaf
          return data.type === 'warning' && (!data.obj.pos_x || !data.obj.pos_y)
        },
      },
      treeClickCount: 0, // 树结构单击次数，用来实现双击
      map: null, // 地图
      defaultOverlayMap: new Map(), // 默认加载覆盖物
      drawingManager: null, // 绘图工具
      styleOptions: {
        strokeColor: 'red', //边线颜色。
        fillColor: 'red', //填充颜色。当参数为空时，圆形将没有填充效果。
        strokeWeight: 2, //边线的宽度，以像素为单位。
        strokeOpacity: 0.5, //边线透明度，取值范围0 - 1。
        fillOpacity: 0.1, //填充的透明度，取值范围0 - 1。
        strokeStyle: 'dashed', //边线的样式，solid或dashed。
      },

      // 显示联系列表
      showContent: false,
      showTree: false,
      showResult: false, // 显示圈选结果
      userResultCount: 0, // 圈选用户数量
      videoResultCount: 0, // 圈选摄像头数量
      suppliesResultCount: 0, // 圈选物资数量
      eventResultCount: 0, // 圈选事件数量
      // 激活菜单
      activeMenu: 'contact',
      showTrack: false,
      trackForm: {
        pageNum: 1,
        pageSize: 200,
        daterange: [],
      },
      trackTotal: 0,
      trackCacheData: [], // 缓存全部数据
      trackData: [], // 表格显示数据
      lushuData: {
        polylines: [],
      }, // 路书
      trackMarker: null, // 路径标记点
      // 通讯录显示呼叫按钮
      callBtnVisiable: false, //会议广播按钮
      intercomBtnVisiable: false, // 对讲编组按钮
      playVideoBtnVisiable: false, // 查视频按钮
      smsBtnVisiable: false, //短信按钮
      audioBtnVisiable: false, // 语音呼叫按钮
      videoBtnVisiable: false, // 视频呼叫按钮
      cameraBtnVisiable: false, // 查监控按钮

      showWebrtcVideo: false, // 远程播放窗口
      webrtc2Sip: null, // webrtc2Sip对象

      /**
       * 摄像头播放
       */
      camaraBoxData: [], // 选中的视频列表
      camaraBoxPlayingData: [], // 正在播放的视频
      showCamaraBox: false, // 显示播放窗口

      /**
       * 视频分发
       */
      dispenseable: true, // 是否可以视频分发
      openDistribution: false, // 打开视频分发接口
      distributionSource: {}, // 视频源

      /**
       * 对讲编组
       */
      openIntercom: false, // 打开对讲编组
      intercomForm: {},

      /**
       * 呼叫数据
       */
      makeCallForm: {}, // 单呼表单
      callingDevice: null, // 主叫号码

      /**
       * 会议
       */
      meetingList: [], // 语音会议列表
      openPreview: false, // 显示会议列表预览窗口
      openCreateMeet: false, //打开会议创建窗口
      currentMeeting: null, // 选择的会议
      meetForm: {
        isAllowSpeak: 'yes',
        meetMode: 'audio',
        callinState: '0',
        callinNum: '',
        callinPwd: '',
      },
      rules: {
        meetName: [
          {
            required: true,
            message: '会议名称不能为空',
            trigger: 'blur',
          },
        ],
        callinNum: [
          { required: true, message: '会议号码不能为空', trigger: 'blur' },
        ],
        callinPwd: [
          { required: true, message: '会议密码不能为空', trigger: 'blur' },
        ],
      },

      /**
       * 短信
       */
      openSms: false, // 打开短信窗口
      smsFormData: {}, // 短信数据

      /**
       * 广播
       */
      openBroadcast: false, // 打开广播窗口
      broadcastFormData: {}, // 广播数据

      /**
       * 选择围栏用户
       */
      openFenceUser: false, // 打开选择围栏用户窗口
      fenceUserFormData: {}, // 选择围栏用户数据
      fenceUserSave: null, // 选择围栏用户保存
      /**
       * 打开电子围栏告警记录
       */
      openFenceAlarm: false, // 打开电子围栏报警历史记录窗口
      fenceAlarmFormData: {}, // 电子围栏数据

      /**
       * 打开告警历史记录窗口
       */
      openWarning: false, // 打开告警历史记录窗口
      warningFormData: {}, // 告警数据

      /**
       * 职员
       */
      employeeForm: null, // 职员表单
      openEmployeeForm: false, // 打开职员窗口
      employeeFormTitle: '', // 窗口标题

      /**
       * 操作员
       */
      operatorForm: null, // 操作员表单
      openOperatorForm: false, // 打开操作员窗口
      operatorFormTitle: '', // 窗口标题

      /**
       * 摄像头
       */
      cameraForm: null, // 摄像头表单
      openCameraForm: false, // 打开摄像头窗口
      cameraFormTitle: '', // 窗口标题

      /**
       * 物资
       */
      suppliesForm: null, // 物资表单
      openSuppliesForm: false, // 打开物资窗口
      suppliesFormTitle: '', // 窗口标题

      /**
       * 事件
       */
      eventTypeOptions: [
        {
          label: '一般',
          value: 1,
        },
        {
          label: '较大',
          value: 2,
        },
        {
          label: '重大',
          value: 3,
        },
        {
          label: '特别重大',
          value: 4,
        },
      ],
      eventForm: null, // 事件表单
      openEventForm: false, // 打开事件窗口
      eventFormTitle: '', // 窗口标题

      /**
       * 告警
       */
      openWarnDialog: false, // 打开告警信息窗口

      /**
       * 触控拖拽
       */
      touchDragObj: null,
      touchDragObj4Webrtc: null,

      /**
       * 点聚合
       */
      videoMarkerCluster: null,
      userMarkerCluster: null,
      eventMarkerCluster: null,
      suppliesMarkerCluster: null,
      warningMarkerCluster: null,

      searchKey: '',

      /**
       * 圈选
       */
      drawingOverlay: null,
      drawingOverlayEvent: null,
      isDrawingResult: false,

      // 是否初始化
      markerClusterInited: false,

      showOverlayTypes: ['contact', 'video', 'event'], // 地图展示的图标
      // showOverlayTypes: [],
      overlayTypes: [
        { label: '人员', value: 'contact' },
        { label: '监控设备', value: 'video' },
        { label: '事件', value: 'event' },
        { label: '告警设备', value: 'warning' },
      ], // 覆盖物选项
    }
  },

  computed: {
    ...mapGetters([
      'hasSuppliesModule',
      'hasEventModule',
      'hasWarningModule',
      'operatorInfo',
      'operatorCallTels',
      'handMicphone',
      'centerAndZoom',
      'videoTreeData',
      'userTreeData',
      'eventTreeData',
      'videoGroupMap',
      'bindingVideoMap',
      'videoMap',
      'groupMap',
      'operatorMap',
      'employeeMap',
      'eventMap',
      'suppliesMap',
      'id2nidMap',
      'deviceStatusMap',
      'locationMap',
      'gpsBHMap',
      'videoTreeMap',
      'suppliesTreeData',
      'associatedMap',
      'warningTreeData',
      'warningEquipmentPartMap',
    ]),
  },
  watch: {
    videoMap() {
      if (this.showOverlayTypes.includes('video'))
        this.initDefaultOverlay('video')
    },
    employeeMap() {
      if (this.showOverlayTypes.includes('contact'))
        this.initDefaultOverlay('employee')
    },
    operatorMap() {
      if (this.showOverlayTypes.includes('contact'))
        this.initDefaultOverlay('operator')
    },
    suppliesMap() {
      if (this.showOverlayTypes.includes('supplies'))
        this.initDefaultOverlay('supplies')
    },
    eventMap() {
      if (this.showOverlayTypes.includes('event'))
        this.initDefaultOverlay('event')
    },
    warningEquipmentPartMap() {
      if (this.showOverlayTypes.includes('warning'))
        this.initDefaultOverlay('warning')
    },
    showOverlayTypes(val, old) {
      console.log('showOverlayTypes', JSON.stringify(val), JSON.stringify(old))
      this.showOverlayChange(val, old)
    },
  },
  methods: {
    // 处理地图工具栏按钮点击事件
    handleCommand(command) {
      console.log('handleCommand', command)
      this.toolCommond = command
      this.isDrawingManagerInited()
      switch (command) {
        case 'hander':
          this.drawingManager.close()
          this.map.enableDoubleClickZoom()
          break
        case 'refresh':
          this.handleRefresh()
          break
        case 'center':
          this.handleCenterAndZoom()
          break
        case 'fullscreen':
          this.handleFullScreen()
          break
        case 'suppliesAlarm':
          this.handleSuppliesWarning()
          break
        case 'eventAlarm':
          break
        case 'fenceAlarm':
          this.viewFenceAlarm()
          break
        case 'warningAlarm':
          this.handleWarning()
          break
        case 'distance':
          this.handleDistance()
          break
        // case 'area':
        //   this.handleArea()
        //   break
        default:
          this.drawingManager.setDrawingMode(command)
          this.drawingManager.open()
          this.map.disableDoubleClickZoom()

          this.clearDrwingData()

          break
      }
    },
    ...mapMutations([
      'SetCenterAndZoom',
      'updateSuppliesForPos',
      'updateLocation',
      'setWebsocketCallback',
      'updateSupplies',
    ]),
    /**
     * 轮询上报事件
     */
    event(e) {
      // if (e.type == 'locationReport')
      console.log('gis地图', e.type, JSON.stringify(e))
      switch (e.type) {
        case 'CreateMeet':
          if (e.result === 'succ') {
            this.meetingPreview()
          }
          break
        case 'MakeCall':
          if (
            e.result === 'succ' &&
            this.makeCallForm &&
            this.makeCallForm.callType === 'video'
          ) {
            if (!Webrtc2Sip.isExistForName(this.makeCallForm.callingDevice)) {
              // 是否启用软电话，不启用则播放对方视频
              SDK.userVideo(this.makeCallForm.calledDevice).then((res) => {
                let user
                let userID = this.makeCallForm.userID
                if (userID) {
                  user =
                    this.operatorMap.get(userID) || this.employeeMap.get(userID)
                }
                this.camaraBoxPlayingData = []
                this.camaraBoxData = [
                  {
                    nid: this.makeCallForm.callingDevice,
                    id: this.makeCallForm.callingDevice,
                    type: 'userVideo',
                    label: user
                      ? user.realName
                      : this.makeCallForm.callingDevice,
                    obj: {
                      videoID: this.makeCallForm.callingDevice,
                      url: res.rtspUrl,
                    },
                  },
                ]
                this.showCamaraBox = true
              })
            }
          } else if (e.result === 'fail') {
            msgError('呼叫失败')
          }
          break
        case 'UserStatus':
          break
        case 'VideoInfoMod':
          if (e.result === 'succ') {
            msgSuccess('修改摄像头信息成功')
          } else if (e.result === 'fail') {
            msgError('修改摄像头信息失败')
          }
          break
        case 'OperatorMod':
          if (e.result === 'succ') {
            msgSuccess('修改操作信息成功')
          } else if (e.result === 'fail') {
            msgError('修改操作信息失败')
          }
          break
        case 'StartVideoDispense':
          this.dispenseEvent(e)
          break
        case 'locationReport': // 位置订阅上报
          this.handleLocation(e)
          break
        case 'LinkVideoOpen': // 打开联动摄像头
          this.handleLinkVideo(e)
          break
        case 'LinkVideoClose': // 关闭联动摄像头
          this.handleLinkVideoClose(e)
          break
        case 'BroadcastCall':
          //广播--语音调度页面联动
          this.handleBroadcastLink(e)
          break
      }
    },
    // 清除圈选事件
    clearDrwingData() {
      if (this.drawingOverlay) this.map.removeOverlay(this.drawingOverlay)
      this.drawingOverlay = null
      this.isDrawingResult = false
      this.drawingOverlayEvent = null
    },
    handleMsg(data, status) {
      console.log(
        '地图处理websocket推送数据 handleMsg',
        status,
        JSON.stringify(data)
      )
      let ol
      if (data.type === 'label') {
        ol = this.defaultOverlayMap.get(data.suppliesId)
        if (ol) {
          ol.setPosition(new BMap.Point(data.pos_x, data.pos_y))
        } else {
          let point = new BMap.Point(data.pos_x, data.pos_y)
          if (
            this.isDrawingResult &&
            !this.isPointInOverlay(point, this.drawingOverlayEvent)
          ) {
            return
          }
          let marker = this.addMarker(
            {
              label: data.suppliesName,
              type: data.type,
              obj: data,
              id: data.suppliesId,
              parentID: data.typeId,
            },
            point
          )
        }
      } else if (data.type === 'sensor') {
        if (!data.reSuppliesId) return
        ol = this.defaultOverlayMap.get(data.reSuppliesId)
        if (ol) {
          if (!CustomInfoBox.isOpen()) {
            this.openInfoBox(ol.node, ol)
          }
        }
      } else if (data.source === 'event') {
        let obj = data.data
        ol = this.defaultOverlayMap.get(obj.eventId)
        let flag = false
        if (ol) {
          console.log('删除' + obj.eventName)
          this.removeMarker(ol, 'event', obj.eventId)
          if (status !== 2) flag = true
        }
        if (status === 0 || flag) {
          let point = new BMap.Point(obj.eventLongitude, obj.eventLatitude)
          // if (
          //   this.isDrawingResult &&
          //   !this.isPointInOverlay(point, this.drawingOverlayEvent)
          // ) {
          //   return
          // }
          this.addMarker(
            {
              label: obj.eventName,
              type: 'event',
              obj: obj,
              id: obj.eventId,
              parentID: obj.eventType,
            },
            point,
            true
          )
        }
      } else if (data.type === 'warning') {
        let obj = data.data
        ol = this.defaultOverlayMap.get(obj.partId)
        if (ol) {
          this.removeMarker(ol, 'warning', obj.partId)
        }
        // if (obj.isOver === 1) return
        let point = new BMap.Point(obj.longitude, obj.latitude)
        // if (
        //   this.isDrawingResult &&
        //   !this.isPointInOverlay(point, this.drawingOverlayEvent)
        // ) {
        //   return
        // }
        this.addMarker(
          {
            label: obj.partName,
            type: 'warning',
            obj: obj,
            id: data.partId,
            parentID: obj.equipmentId,
          },
          point,
          obj.isOver !== 1
        )
      }
    },
    // 打开联动摄像头
    handleLinkVideo(e) {
      let nid = this.id2nidMap.get(e.videoID)
      if (!nid) return
      this.$nextTick(() => {
        let tree = this.$refs.videoTree
        if (tree) {
          const node = tree.getNode({ nid })
          if (!node) return
          // console.log('打开联动摄像头', node.label)
          if (node) this.playVideoBefore(node.data)
        }
      })
    },
    // 关闭联动摄像头
    handleLinkVideoClose(e) {
      let ref = this.$refs.camaraBox
      if (ref) {
        ref.videoExit('', e.videoID)
      }
    },
    // 处理订阅位置
    handleLocation(e) {
      this.updateLocation(e.pos)
      const data = this.gpsBHMap.get(e.pos.deviceCode)
      if (!data) return
      let id = data.operatorID || data.employeeID || data.videoID
      let marker = this.defaultOverlayMap.get(id)

      if (marker) {
        this.removeMarker(marker, 'employee', id)
      }
      if (!this.showOverlayTypes.includes('contact')) return

      let point = new BMap.Point(data.pos_x, data.pos_y)
      if (
        this.isDrawingResult &&
        !this.isPointInOverlay(point, this.drawingOverlayEvent)
      ) {
        return
      }
      let type = data.operatorID
        ? 'operator'
        : data.employeeID
        ? 'employee'
        : 'video'
      let groupID =
        type === 'operator' ? data.groupID : data.group[0].groupID || ''
      if (!groupID) return
      let nids = this.id2nidMap.get(id)
      this.addMarker(
        {
          id: id,
          label: data.realName || data.videoName,
          type: type,
          obj: data,
          parentID: groupID,
          nid: nids ? nids[0] : '',
        },
        point
      )
    },
    // 显示覆盖物
    showOverlayChange(newTypes, oldTypes) {
      CustomInfoBox.closeBox()
      if (newTypes.length === 0) {
        this.clearAllOverlay()
        return
      }
      if (oldTypes.length > 0) {
        oldTypes.forEach((old) => {
          if (!newTypes.some((type) => type === old)) {
            this.clearClusterMarkers(old)
          }
        })
      }

      newTypes.forEach((type) => {
        if (oldTypes.length === 0 || !oldTypes.some((old) => old === type)) {
          console.log('添加', type)
          if (type === 'contact') {
            this.initDefaultOverlay('operator')
            this.initDefaultOverlay('employee')
          } else {
            this.initDefaultOverlay(type)
          }
        }
      })
    },
    // 清空覆盖物
    clearClusterMarkers(type) {
      let markers
      switch (type) {
        case 'contact':
          if (this.userMarkerCluster) {
            markers = this.userMarkerCluster.getMarkers()
            this.userMarkerCluster.clearMarkers()
          }
          break
        case 'video':
          if (this.videoMarkerCluster) {
            markers = this.videoMarkerCluster.getMarkers()
            this.videoMarkerCluster.clearMarkers()
          }
          break
        case 'supplies':
          if (this.suppliesMarkerCluster) {
            markers = this.suppliesMarkerCluster.getMarkers()
            this.suppliesMarkerCluster.clearMarkers()
          }

          break
        case 'event':
          if (this.eventMarkerCluster) {
            markers = this.eventMarkerCluster.getMarkers()
            this.eventMarkerCluster.clearMarkers()
          }
          break
        case 'warning':
          if (this.warningMarkerCluster) {
            markers = this.warningMarkerCluster.getMarkers()
            this.warningMarkerCluster.clearMarkers()
          }
          break
        default:
          break
      }
      if (markers && markers.length > 0) {
        markers.forEach((m) => {
          m.data && m.data.id && this.defaultOverlayMap.delete(m.data.id)
        })
      }
    },
    // 清空覆盖物
    clearAllClusterMarkers() {
      if (this.videoMarkerCluster) this.videoMarkerCluster.clearMarkers()
      if (this.userMarkerCluster) this.userMarkerCluster.clearMarkers()
      if (this.eventMarkerCluster) this.eventMarkerCluster.clearMarkers()
      if (this.warningMarkerCluster) this.warningMarkerCluster.clearMarkers()
      if (this.suppliesMarkerCluster) this.suppliesMarkerCluster.clearMarkers()
    },
    // 通过类型初始化地图标注
    initDefaultOverlayByTypes(types = this.showOverlayTypes) {
      this.clearAllOverlay()
      types.forEach((type) => {
        if (type === 'contact') {
          this.initDefaultOverlay('operator')
          this.initDefaultOverlay('employee')
        } else {
          this.initDefaultOverlay(type)
        }
      })
    },
    // 树结构间隔
    getIndent() {
      return this.scalePx(16)
    },
    // 获取等比例px
    scalePx(px) {
      let width = document.body.clientWidth
      return (px * width) / 1920
    },
    // 视频分发事件
    dispenseEvent(e) {
      if (e.result === 'fail') {
        return msgError(`视频分发失败, ${e.reason}`)
      }
      let msgStr = sessionStorage.getItem(e.mesg_id)
      if (msgStr) {
        const dispenseData = JSON.parse(msgStr)
        dispenseData.flowID = e.eventdata.flowID
        let dispense = []
        let old = sessionStorage.getItem(
          `dispense-${dispenseData.calledDevice || dispenseData.videoID}`
        )
        if (old) dispense = JSON.parse(old)
        dispense.push(dispenseData)
        sessionStorage.setItem(
          `dispense-${dispenseData.calledDevice || dispenseData.videoID}`,
          JSON.stringify(dispense)
        )
        sessionStorage.removeItem(e.mesg_id)
      }
    },
    // 格式化日期
    formatterTime(timeStr) {
      return parseStr2DateStr(timeStr)
    },
    // 格式化小数
    formatStr(num) {
      if (!Number.isFinite(Number.parseFloat(num))) return ''
      return Number.parseFloat(num).toFixed(3)
    },
    // 时间选择框确定
    daterangeChange(daterange) {
      if (daterange[1] - daterange[0] > 24 * 3600 * 1000) {
        daterange[1].setTime(daterange[0].getTime() + 24 * 3600 * 1000)
      }
    },
    // 获取组名
    getGroupName(node) {
      let pNode
      if (node.type === 'operator' || node.type === 'employee') {
        if (this.groupMap !== null) {
          pNode = this.groupMap.get(node.parentID)
        }
      } else if (node.type === 'video') {
        if (this.videoGroupMap !== null) {
          pNode = this.videoGroupMap.get(node.parentID)
        }
      }
      return pNode ? pNode.label : ''
    },
    // 打开物资告警消息
    handleSuppliesWarning() {
      this.openWarnDialog = true
    },
    // 测距
    handleDistance() {
      let dis = new BMapLib.DistanceTool(this.map)
      // 打开
      dis.open()
    },
    // 测面积
    handleArea() {
      let area = new CustomAreaTool(this.map)
      // 打开
      area.open()
    },
    // 打开告警记录
    handleWarning(partId) {
      this.warningFormData = { partId }
      this.openWarning = true
    },
    // 是否可以发送短信
    hasSms(data) {
      if (data.type === 'employee') {
        let obj = data.obj
        let deviceObj = obj.device.find(
          (item) => item.deviceType === 'poc' || item.deviceType === 'mobile'
        )
        if (deviceObj) return deviceObj
      }
      return false
    },
    // 移除按钮
    handleRemove(data) {
      data.obj.pos_x = ''
      data.obj.pos_y = ''

      this.removeMarkerByNode(data)
      this.infoModify(data)
    },
    // 标注按钮
    handleMark(data) {
      this.currentNodeData = data
      console.log('handleMark data =' + JSON.stringify(data))
      // this.$message("handleMark data =" + JSON.stringify(data));
      this.isDrawingManagerInited()
      this.drawingManager.setDrawingMode(BMAP_DRAWING_MARKER)
      this.drawingManager.open()
    },
    // 修改位置信息
    infoModify(data) {
      if (data.children) return
      console.log('修改位置信息', data)
      try {
        switch (data.type) {
          case 'video':
            SDK.videoInfoMod(data.obj)
            break
          case 'employee':
            SDK.employeeMod(data.obj)
            break
          case 'operator':
            SDK.operatorMod(data.obj)
            break
          case 'label':
            let props,
              xFlag = true,
              yFlag = true
            props = JSON.parse(data.obj.suppliesProp)
            if (props) {
              for (const item of props) {
                if (item.propName === '经度') {
                  item.propData = data.obj.pos_x
                  xFlag = false
                }
                if (item.propName === '纬度') {
                  item.propData = data.obj.pos_y
                  yFlag = false
                }
              }
            } else {
              props = []
            }
            data.obj.suppliesProp = JSON.stringify(props)
            modifySupplies(data.obj)
            break
          case 'event':
            data.obj.eventLongitude = data.obj.pos_x
            data.obj.eventLatitude = data.obj.pos_y
            putObj(data.obj)
            break
          default:
            break
        }
      } catch (err) {
        msgError('操作失败')
        this.removeMarkerByNode(data)
      }
    },
    // 点击节点
    nodeClick(data, type) {
      if (data.children || (!data.obj.pos_x && !data.obj.pos_y)) return
      //记录点击次数
      this.treeClickCount++
      //单次点击次数超过2次不作处理,直接返回,也可以拓展成多击事件
      if (this.treeClickCount >= 2) {
        return
      }
      //计时器,计算300毫秒为单位,可自行修改
      this.timer = window.setTimeout(() => {
        // console.log('nodeClick', JSON.stringify(data))
        if (this.treeClickCount == 1) {
          //把次数归零
          this.treeClickCount = 0
          //单击事件处理
          let marker = this.getMarkerByNodeAndSetZIndex(data)
          if (!marker) return
          let cluster = this.getCluster(marker)
          if (cluster) {
            this.openInfoBox(data, marker, cluster)
            this.map.panTo(cluster.getCenter())
          } else {
            marker.setZIndex(1)
            this.openInfoBox(data, marker)
            this.map.panTo(marker.getPosition())
          }
        } else if (this.treeClickCount > 1) {
          //把次数归零
          this.treeClickCount = 0
          //双击事件
          let marker = this.getMarkerByNodeAndSetZIndex(data)
          if (!marker) {
            let point
            if (
              type === 'event' &&
              data.obj.eventLongitude &&
              data.obj.eventLatitude
            ) {
              point = new BMap.Point(
                data.obj.eventLongitude,
                data.obj.eventLatitude
              )
            } else if (data.obj.pos_x && data.obj.pos_y) {
              point = new BMap.Point(data.obj.pos_x, data.obj.pos_y)
            } else {
              return
            }
            marker = this.addMarker(data, point)
          }
          let cluster = this.getCluster(marker)
          if (cluster) {
            this.openInfoBox(data, marker, cluster)
            this.map.panTo(cluster.getCenter())
          } else {
            marker.setZIndex(1)
            this.openInfoBox(data, marker)
            this.map.panTo(marker.getPosition())
          }
        }
      }, 300)
    },

    // 对讲编组按钮
    handleIntercom() {
      let rows = this.$refs.userTree.getCheckedNodes(true)
      if (rows.length === 0) {
        msgError('请至少选择一个人员')
        return
      }
      if (!this.handMicphone) {
        msgError('操作员不存在手咪，无法呼叫对讲编组')
        return
      }
      const callingDevice = this.handMicphone
      let called = []
      for (let i = 0; i < rows.length; i++) {
        const node = rows[i]
        let obj = node.obj
        let telStatus, tel
        if (node.type === 'operator') {
          // msgError(`操作员 ${node.label} 不进行对讲编组`)
          console.log(`操作员 ${node.label} 不进行对讲编组`)
          continue
        } else if (
          node.type === 'employee' &&
          (obj.deviceType === 'poc' || obj.deviceType === 'esip')
        ) {
          tel = obj.defaultNo
        } else {
          console.log(`用户 ${node.label} 不存在手咪，无法呼叫对讲编组`)
        }
        tel = obj.defaultNo
        if (tel) {
          // let telstatus = this.deviceStatusMap.get(tel)
          let telstatus = 'online'
          if (telstatus !== 'offline') {
            called.push({
              calledDevice: tel,
              callType: 'join',
            })
          } else {
            console.log(`用户 ${node.label}号码${tel}不在线，无法呼叫对讲编组`)
          }
        }
      }
      if (called.length === 0) {
        return msgError(`被叫号码不能为空`)
      }
      this.intercomForm = {
        callingDevice,
        called,
        callingMode: 'once',
        Time: 1,
      }
      this.openIntercom = true
    },
    // 对讲编组
    selectCallEx() {
      if (
        this.intercomForm.callingMode === 'temporary' &&
        !this.intercomForm.Time
      ) {
        return msgError(`请输入时间`)
      }
      this.$store.commit('callFlagEvent', true) // vuex缓存
      SDK.selectCallEx(this.intercomForm).then((res) => {
        console.log('对讲编组', JSON.stringify(res))
        this.openIntercom = false
      })
    },
    // 返回按钮
    handleTrackClear() {
      this.showTrack = false
      this.showContent = true
      this.showTree = true
      this.clearLushu()
    },
    // 清除路书
    clearLushu() {
      this.trackCacheData = []
      this.trackData = []
      this.trackTotal = 0
      if (this.lushuData.lushu) {
        this.map.removeOverlay(this.lushuData.startMarker)
        this.map.removeOverlay(this.lushuData.endMarker)
        this.lushuData.polylines.forEach((item) => {
          this.map.removeOverlay(item)
        })
        this.map.removeOverlay(this.lushuData.polyline)
        this.lushuData.lushu.clear()
        this.lushuData = {
          polylines: [],
        }
      }
    },
    // 添加路书起点终点标注
    addLushuMarker(point, type) {
      let imgurl
      switch (type) {
        case 'start':
          imgurl = 'static/bmap/images/start-point.png'
          break
        case 'end':
          imgurl = 'static/bmap/images/end-point.png'
          break
        case 'track':
          imgurl = 'static/bmap/images/marker-blue.png'
          break
      }
      let icon = new BMap.Icon(imgurl, new BMap.Size(30, 30), {
        anchor: new BMap.Size(15, 30),
        imageSize: new BMap.Size(30, 30),
        infoWindowAnchor: new BMap.Size(15, 0),
      })
      let marker = new CustomMarker(
        {
          point,
        },
        {
          icon,
        }
      )
      // 创建标注
      this.map.addOverlay(marker)
      marker.setZIndex(1)
      return marker
    },
    // 设置视野
    setViewport() {
      if (this.map) this.map.setViewport(this.ckeckedPoints)
    },
    // 查轨迹
    handleTrack(node) {
      this.trackForm.data = node
      this.trackForm.deviceCode = node.obj.gpsBH
      this.trackForm.pageNum = 1
      let end = new Date()
      let start = new Date(end.valueOf() - 24 * 60 * 60 * 1000)
      this.trackForm.daterange = [start, end]
      this.showTree = false
      this.showContent = true
      this.showTrack = true
    },
    // 查询历史定位
    queryTrack() {
      this.clearLushu()
      let form = this.trackForm
      form.start = parseTime2Str(this.trackForm.daterange[0])
      form.end = parseTime2Str(this.trackForm.daterange[1])
      let warning = msgWarning('轨迹查询数据量大，请耐心等待')

      SDK.historyLocation(form)
        .then((res) => {
          warning.close()
          let wzs = (this.trackCacheData = res.WZ)
          this.trackTotal = wzs.length
          this.queryCacheTrack()
          let arrPois = []
          let polylinePoins = []
          let point, lastPoint
          for (let i = 0; i < wzs.length; i++) {
            let time = parseStr2Date(wzs[i].SJ)
            if (i !== 0) {
              let lastTime = parseStr2Date(wzs[i - 1].SJ)
              if (
                Math.round(
                  (time.getTime() - lastTime.getTime()) / (1000 * 60)
                ) > 5
              ) {
                let polyline = new BMap.Polyline(polylinePoins, {
                  strokeColor: '#4169E1',
                })
                this.map.addOverlay(polyline)
                this.lushuData.polylines.push(polyline)
                polylinePoins = []
                lastPoint = undefined
              }
            }
            point = new BMap.Point(wzs[i].X, wzs[i].Y)
            if (
              lastPoint &&
              lastPoint.lng === point.lng &&
              lastPoint.lat === point.lat
            ) {
              continue
            } else {
              lastPoint = point
            }
            polylinePoins.push(point)
            arrPois.push(point)
          }

          console.log('arrPois.length=', arrPois.length)
          let polyline = new BMap.Polyline(polylinePoins, {
            strokeColor: '#4169E1',
          })
          this.map.addOverlay(polyline)
          this.lushuData.polylines.push(polyline)

          let start = new BMap.Point(wzs[0].X, wzs[0].Y)
          let end = new BMap.Point(wzs[wzs.length - 1].X, wzs[wzs.length - 1].Y)
          this.lushuData.startMarker = this.addLushuMarker(start, 'start')
          this.lushuData.endMarker = this.addLushuMarker(end, 'end')

          this.map.setViewport(arrPois)

          // 计算距离和速度，默认60s走完
          let distance = BMapLib.GeoUtils.getPolylineDistance(arrPois)
          let speed = parseInt(distance / 30)
          console.log('lushu distance=', distance, ',speed=', speed)

          let lushu = (this.lushuData.lushu = new BMapLib.LuShu(
            this.map,
            arrPois,
            {
              defaultContent: this.trackForm.data.label,
              autoView: true, //是否开启自动视野调整，如果开启那么路书在运动过程中会根据视野自动调整
              icon: new BMap.Icon(
                '/static/bmap/images/marker-green.png',
                new BMap.Size(30, 30),
                {
                  anchor: new BMap.Size(15, 30),
                  imageSize: new BMap.Size(30, 30),
                }
              ),
              speed: speed,
              enableRotation: false, //是否设置marker随着道路的走向进行旋转
              landmarkPois: [],
            }
          ))
          lushu.start()

          // //绑定事件
          // $("run").onclick = function () {
          //   lushu.start();
          // }
          // $("stop").onclick = function () {
          //   lushu.stop();
          // }
          // $("pause").onclick = function () {
          //   lushu.pause();
          // }
          // $("hide").onclick = function () {
          //   lushu.hideInfoWindow();
          // }
          // $("show").onclick = function () {
          //   lushu.showInfoWindow();
          // }
          // function $ (element) {
          //   return document.getElementById(element);
          // }
        })
        .catch((err) => {
          warning.close()
          console.log('轨迹查询失败', err)
          if (err.reason === 'The_device_has_no_location_information') {
            msgError('没有定位信息')
          } else {
            msgError('轨迹查询失败')
          }
        })
    },
    // 从缓存中获取轨迹渲染表格
    queryCacheTrack() {
      if (!this.trackCacheData || this.trackCacheData.length === 0) {
        this.trackData = []
      }
      let startIndex = (this.trackForm.pageNum - 1) * this.trackForm.pageSize
      let endIndex = this.trackForm.pageNum * this.trackForm.pageSize
      this.trackData = this.trackCacheData.slice(startIndex, endIndex)
    },
    // 会议预览
    meetingPreview() {
      SDK.getMeetingList()
        .then((res) => {
          this.meetingList = res.meet_list.filter(
            (item) => item.meetMode !== 'monitor'
          )
          if (!this.currentMeeting && this.meetingList.length > 0) {
            this.currentMeeting = this.meetingList[0]
          }
          this.meetingList.forEach((item, index, arr) => {
            SDK.getMeetingMember(item.meetID).then((res) => {
              item.memberSize = res.meet_list.length
              this.$set(arr, index, item)
            })
          })
          this.openPreview = true
        })
        .catch((err) => {
          console.log('会议预览失败', err)
        })
    },
    // 设置当前会议
    setCurrentMeet(meeting) {
      this.currentMeeting = meeting
      sessionStorage.setItem('currentMeeting', JSON.stringify(meeting))
    },
    // 新建会议按钮
    handleCreateMeet() {
      this.meetForm = {
        isAllowSpeak: 'yes',
        meetMode: 'audio',
      }
      this.openCreateMeet = true
    },
    // 新建会议
    createMeet() {
      this.$refs['meetForm'].validate((valid) => {
        if (valid) {
          this.meetForm.meetNum = this.meetForm.meetName
          SDK.createMeet(this.meetForm)
            .then((res) => {
              this.openCreateMeet = false
            })
            .catch((err) => msgError('创建会议失败'))
        }
      })
    },
    // 加入会议
    joinMeet() {
      if (!this.currentMeeting) {
        msgError('请选择会议')
        return
      }
      try {
        let rows = this.$refs.userTree.getCheckedNodes(true)
        if (!this.callingDevice) {
          if (!this.operatorCallTels) return msgError(`操作员号码不可达`)
          this.callingDevice = this.operatorCallTels[0]
        }
        let calleds = []
        rows.forEach((item) => {
          if (item.id !== this.operatorInfo.operatorID) {
            if (item.type === 'operator') {
              if (item.obj.mainTel && item.obj.mainTelType === 'phone')
                calleds.push(item.obj.mainTel)
              else if (item.obj.viceTel && item.obj.viceTelType === 'phone')
                calleds.push(item.obj.viceTel)
            } else if (item.type === 'employee' && item.obj.defaultNo) {
              calleds.push(item.obj.defaultNo)
            }
          }
        })
        if (calleds.length === 0) {
          msgError('被叫号码为空')
          return
        }
        SDK.meetingCall(this.currentMeeting.meetID, calleds, this.callingDevice)
          .then((res) => {
            this.openPreview = false
            this.setCurrentMeet(this.currentMeeting)

            this.$router.push({ name: 'meeting' })
          })
          .catch((err) => {
            console.log('加入会议失败', err)
          })
      } catch (error) {
        console.log('加入会议失败', error)
      }
    },
    // 对物资进行负责人单呼
    makeCallForSupplies(person, callType) {
      this.makeCallForm = null
      let telStatus = this.deviceStatusMap.get(person.personTel)
      if (telStatus === 'offline')
        return msgError(`${person.personTel}号码不在线`)
      if (this.operatorCallTels.includes(person.personTel))
        return msgError(`主被叫号码相同`)
      if (!this.callingDevice) {
        if (!this.operatorCallTels) return msgError(`操作员号码不可达`)
        this.callingDevice = this.operatorCallTels[0]
      }
      this.makeCallForm = {
        callingDevice: this.callingDevice,
        calledDevice: person.personTel,
        callType: callType,
        userID: '',
      }
      this.$store.commit('callFlagEvent', true) // vuex缓存
      SDK.makeCall(this.makeCallForm)
        .then((res) => {})
        .catch((err) => {
          msgError(typeof err === 'object' ? err.reason : err)
          this.$store.commit('callFlagEvent', false) // vuex缓存
        })
    },
    // 对事件上报人单呼
    makeCallForEvent(userTel, callType) {
      this.makeCallForm = null
      let telStatus = this.deviceStatusMap.get(userTel)
      if (telStatus === 'offline') return msgError(`${userTel}号码不在线`)
      if (this.operatorCallTels.includes(userTel))
        return msgError(`主被叫号码相同`)
      if (!this.callingDevice) {
        if (!this.operatorCallTels) return msgError(`操作员号码不可达`)
        this.callingDevice = this.operatorCallTels[0]
      }
      this.makeCallForm = {
        callingDevice: this.callingDevice,
        calledDevice: userTel,
        callType: callType,
        userID: '',
      }
      SDK.makeCall(this.makeCallForm)
        .then((res) => {})
        .catch((err) => {
          msgError(typeof err === 'object' ? err.reason : err)
        })
      this.$store.commit('callFlagEvent', true) // vuex缓存
    },
    // 查监控
    handleViewCamara() {
      let rows = this.$refs.userTree.getCheckedNodes(true)
      if (
        rows.length === 0 ||
        !rows[0].obj.videoID ||
        rows[0].obj.videoID.length === 0
      ) {
        msgError('请选择绑定有摄像头的人员')
        return
      }
      this.playBindingVideo(rows[0].obj.videoID)
    },
    // 节点选中呼叫
    handleMakeCall(type) {
      let rows = this.$refs.userTree.getCheckedNodes(true)
      if (rows.length === 0) {
        msgError('请选择人员')
        return
      }
      this.makeCall(rows[0], type)
    },
    // 单呼
    makeCall(data, callType) {
      if (!data.obj) data = { obj: data }
      console.log('单呼', JSON.stringify(data), 'callType=' + callType)
      this.makeCallForm = null
      let telStatus, tel
      if (callType === 'half-audio') {
        telStatus = this.deviceStatusMap.get(this.handMicphone)
        if (telStatus === 'offline') {
          msgError(`操作员手咪${this.handMicphone}不在线`)
          return
        }
        tel = data.obj.defaultNo
        if (!tel) return msgError(`${data.label}默认号码不存在`)
        telStatus = this.deviceStatusMap.get(tel)
        if (telStatus === 'offline') {
          msgError(`${tel}号码不在线`)
          return
        }
        this.makeCallForm = {
          callingDevice: this.handMicphone,
          calledDevice: tel,
          userID: data.id,
          callType: 'audio',
          duplexMode: 'half',
        }
      } else {
        if (!this.callingDevice) {
          if (!this.operatorCallTels) return msgError(`操作员号码不可达`)
          this.callingDevice = this.operatorCallTels[0]
        }

        if (data.type === 'operator') {
          tel = data.obj.mainTel || data.obj.viceTel
          if (!tel) return msgError(`${data.label}没有手柄号码`)
          if (this.operatorCallTels.includes(tel))
            return msgError('主被叫号码相同')
          telStatus = this.deviceStatusMap.get(tel)
          if (telStatus === 'offline') {
            msgError(`操作员手柄${tel}不在线`)
            return
          }
        } else if (data.type === 'employee') {
          tel = data.obj.defaultNo
          if (!tel) return msgError(`${data.label}默认号码不存在`)
          telStatus = this.deviceStatusMap.get(tel)
          if (telStatus === 'offline') {
            msgError(`${tel}号码不在线`)
            return
          }
        }
        this.makeCallForm = {
          callingDevice: this.callingDevice,
          calledDevice: tel,
          callType: callType || 'audio',
          userID: data.id || '',
        }
      }

      SDK.makeCall(this.makeCallForm)
        .then((res) => {})
        .catch((err) => {
          msgError(typeof err === 'object' ? err.reason : err)
        })
      this.$store.commit('callFlagEvent', true) // vuex缓存
    },
    // 短信按钮
    handleSms(node) {
      let rows = node ? [node] : this.$refs.userTree.getCheckedNodes(true)
      this.smsFormData = {}

      let receiver = new Set()
      let contacts = new Set()
      rows.forEach((item) => {
        if (item.type === 'operator') {
          receiver.add(item.label)
          contacts.add(item.id)
        } else {
          if (
            item.obj.deviceType === 'poc' ||
            item.obj.deviceType === 'mobile'
          ) {
            receiver.add(item.label)
            contacts.add(item.obj.defaultNo)
          } else {
            let deviceObj = this.hasSms(item)
            if (deviceObj) {
              receiver.add(item.label)
              contacts.add(deviceObj.deviceTel)
            }
          }
        }
      })
      if (contacts.size === 0) {
        msgError('能接收短信的终端不存在')
        return
      }
      this.smsFormData.receiver = [...receiver.values()]
      this.smsFormData.contacts = [...contacts.values()]
      this.openSms = true
    },
    // 广播按钮
    handleBroadCast(node) {
      let rows = node ? [node] : this.$refs.userTree.getCheckedNodes(true)
      this.broadcastFormData = {}

      let receiver = new Set()
      let contacts = new Map()
      rows.forEach((item) => {
        if (item.type === 'operator') {
          if (item.obj.mainTelType === 'phone' && item.obj.mainTel) {
            receiver.add(item.label)
            contacts.set(item.id, {
              calledDevice: item.obj.mainTel,
              userID: item.id,
            })
          } else if (item.obj.viceTelType === 'phone' && item.obj.viceTel) {
            receiver.add(item.label)
            contacts.set(item.id, {
              calledDevice: item.obj.viceTel,
              userID: item.id,
            })
          }
        } else {
          receiver.add(item.label)
          contacts.set(item.id, {
            calledDevice: item.obj.defaultNo,
            userID: item.id,
          })
        }
      })
      if (contacts.size === 0) {
        msgError('能接听广播的终端不存在')
        return
      }
      this.broadcastFormData.receiver = [...receiver.values()]
      this.broadcastFormData.contacts = contacts
      this.openBroadcast = true
    },
    // 会议
    handleMeeting() {
      console.log('handleMeeting')
      let rows = this.$refs.userTree.getCheckedNodes(true)
      if (rows.length === 0) {
        msgError('请至少选择一个人员')
        return
      }
      this.meetingPreview()
    },
    // 节点过滤，e为画图事件
    handleUserFilter(e = '', data) {
      if (data.type !== 'group') {
        let isMarker = this.showOverlayTypes.includes('contact')
        if (typeof e === 'string') {
          let flag = false
          if (data.type === 'operator') {
            if (
              data.label.includes(e) ||
              data.obj.mainTel.includes(e) ||
              data.obj.viceTel.includes(e)
            ) {
              flag = true
            }
          } else if (data.label.includes(e) || data.obj.defaultNo.includes(e)) {
            flag = true
          }
          if (flag) {
            this.userResultCount++
            if (isMarker && data.obj.pos_x && data.obj.pos_y) {
              this.addMarker(
                data,
                new BMap.Point(data.obj.pos_x, data.obj.pos_y)
              )
            }
          }
          return flag
        } else if (data.obj.pos_x && data.obj.pos_y) {
          let point = new BMap.Point(data.obj.pos_x, data.obj.pos_y)
          if (this.isPointInOverlay(point, e)) {
            isMarker && this.addMarker(data, point)
            this.userResultCount++
            return true
          }
        }
      }
      return false
    },
    // 节点过滤，e为画图事件或者检索字符串
    handleVideoFilter(e = '', data) {
      if (data.type !== 'videogroup') {
        let isMarker = this.showOverlayTypes.includes('video')
        if (typeof e === 'string') {
          if (data.label.includes(e)) {
            this.videoResultCount++
            if (isMarker && data.obj.pos_x && data.obj.pos_y) {
              this.addMarker(
                data,
                new BMap.Point(data.obj.pos_x, data.obj.pos_y)
              )
            }
            return true
          }
        } else if (data.obj.pos_x && data.obj.pos_y) {
          let point = new BMap.Point(data.obj.pos_x, data.obj.pos_y)
          if (this.isPointInOverlay(point, e)) {
            isMarker && this.addMarker(data, point)
            this.videoResultCount++
            return true
          }
        }
      }
      return false
    },
    // 节点过滤，e为画图事件或者检索字符串
    handleSuppliesFilter(e = '', data) {
      if (data.type !== 'suppliesgroup') {
        let isMarker = this.showOverlayTypes.includes('supplies')
        if (typeof e === 'string') {
          if (data.label.includes(e)) {
            this.suppliesResultCount++
            if (isMarker && data.obj.pos_x && data.obj.pos_y) {
              this.addMarker(
                data,
                new BMap.Point(data.obj.pos_x, data.obj.pos_y)
              )
            }
            return true
          }
        } else if (data.obj.pos_x && data.obj.pos_y) {
          let point = new BMap.Point(data.obj.pos_x, data.obj.pos_y)
          if (this.isPointInOverlay(point, e)) {
            isMarker && this.addMarker(data, point)
            this.suppliesResultCount++
            return true
          }
        }
      }
      return false
    },
    // 节点过滤，e为画图事件或者检索字符串
    handleEventFilter(e = '', data) {
      if (data.type !== 'eventgroup') {
        let isMarker = this.showOverlayTypes.includes('event')
        if (typeof e === 'string') {
          if (data.label.includes(e)) {
            this.eventResultCount++
            if (isMarker && data.obj.eventLongitude && data.obj.eventLatitude) {
              this.addMarker(
                data,
                new BMap.Point(data.obj.eventLongitude, data.obj.eventLatitude)
              )
            }
            return true
          }
        } else if (data.obj.eventLongitude && data.obj.pos_y) {
          let point = new BMap.Point(
            data.obj.eventLongitude,
            data.obj.eventLatitude
          )
          if (this.isPointInOverlay(point, e)) {
            isMarker && this.addMarker(data, point)
            this.eventResultCount++
            return true
          }
        }
      }
      return false
    },
    // 节点过滤，e为画图事件或者检索字符串
    handleWarningFilter(e = '', data) {
      if (data.type !== 'warning') {
        let isMarker = this.showOverlayTypes.includes('warning')
        if (typeof e === 'string') {
          if (data.label.includes(e)) {
            this.eventResultCount++
            if (isMarker && data.obj.eventLongitude && data.obj.eventLatitude) {
              this.addMarker(
                data,
                new BMap.Point(data.obj.eventLongitude, data.obj.eventLatitude)
              )
            }
            return true
          }
        } else if (data.obj.eventLongitude && data.obj.pos_y) {
          let point = new BMap.Point(
            data.obj.eventLongitude,
            data.obj.eventLatitude
          )
          if (this.isPointInOverlay(point, e)) {
            isMarker && this.addMarker(data, point)
            this.eventResultCount++
            return true
          }
        }
      }
      return false
    },
    // 退出视频
    videoExit(index, webrtc2Sip) {
      let calledDevice = this.makeCallForm.calledDevice || ''
      if (index === '_remote') {
        this.handleHangup(webrtc2Sip)
      }
      // 退出分发
      let old = sessionStorage.getItem(`dispense-${calledDevice}`)
      if (old) {
        let datas = JSON.parse(old)
        datas.forEach((item) => {
          SDK.stopVideoDispense({
            flowID: item.flowID,
            calledDevice,
          })
        })
        sessionStorage.removeItem(`dispense-${calledDevice}`)
      }
    },
    // 播放视频
    handlePlayVideo() {
      let nodes = this.$refs.videoTree.getCheckedNodes(true)
      if (nodes.length === 0) {
        msgError('请至少选择一个摄像头')
        return
      }
      let rows = nodes.filter((node) => node.type === 'video')
      if (rows.length === 0) {
        msgError('选择目录的摄像头为空')
        return
      }
      this.camaraBoxPlayingData = []
      this.camaraBoxData = rows
      this.showCamaraBox = true
    },
    // 播放绑定摄像头
    playBindingVideo(videoIds) {
      console.log('播放绑定摄像头', videoIds)

      let rows = []
      videoIds.forEach((item) => {
        let video = this.videoMap.get(item)
        let nids = this.id2nidMap.get(item)
        if (video && nids) {
          rows.push({
            obj: video,
            type: 'video',
            label: video.videoName,
            id: video.videoID,
            nid: nids[0],
          })
        }
      })
      if (rows.length > 0) {
        this.camaraBoxPlayingData = []
        this.camaraBoxData = rows
        this.showCamaraBox = true
      }
    },
    //  播放视频前处理
    playVideoBefore(node) {
      this.camaraBoxPlayingData = []
      this.camaraBoxData = [node]
      this.showCamaraBox = true
    },
    // 打开视频分发窗口
    openVideoDistribution(data) {
      console.log('视频分发')
      this.distributionSource =
        data === '_remote'
          ? {
              type: 'userVideo',
              videoID: this.callingDevice || this.operatorCallTels[0],
            }
          : data
      this.openDistribution = true
    },
    // 当前行改变
    trackPointClick(cur) {
      if (this.trackMarker) this.map.removeOverlay(this.trackMarker)
      if (!cur) return
      this.trackMarker = this.addLushuMarker(
        new BMap.Point(cur.posX, cur.posY),
        'track'
      )
    },
    // 清除圈选
    handleClear() {
      if (this.isDrawingResult) {
        this.clearDrwingData()
      } else {
        this.searchKey = ''
      }
      this.handleQuery()
    },
    // 列表按钮
    handleList() {
      this.showContent = !this.showContent
      if (!this.showTree && !this.showTrack) this.showTree = true
    },
    // 默认选择结果
    resetResultCount() {
      this.userResultCount = 0
      this.videoResultCount = 0
      this.suppliesResultCount = 0
    },
    // 设置tree未选中状态
    setUnchecked(ref) {
      const tree = this.$refs[ref]
      if (tree) {
        let rows = tree.getCheckedNodes(true)
        if (rows.length === 0) {
          return
        } else {
          rows.forEach((item) => {
            tree.setChecked(item, false, true)
          })
        }
      }
    },
    // 重置选中行
    resetChecked() {
      this.setUnchecked('userTree')
      this.setUnchecked('videoTree')
      if (this.hasSuppliesModule) {
        this.setUnchecked('suppliesTree')
      }
      if (this.hasEventModule) {
        this.setUnchecked('eventTree')
      }
    },
    // 重置搜索查询
    resetFilter(e) {
      const filter = (ref) => {
        this.$refs[ref].filter(e)
      }
      filter('userTree')
      filter('videoTree')
      if (this.hasSuppliesModule) {
        filter('suppliesTree')
      }
      if (this.hasEventModule) {
        filter('eventTree')
      }
    },
    // 重置树结果
    resetTree(e) {
      this.resetResultCount()
      this.resetChecked()
      this.resetFilter(e)
    },
    // 搜索按钮
    handleQuery() {
      if (this.isDrawingResult) {
        return
      }
      this.clearAllOverlay()
      this.resetTree(this.searchKey)
      this.showResult = this.searchKey ? true : false
      this.showTrack = false
      this.showContent = true
      this.showTree = true
    },
    // 点击视频节点复选框
    clickCheckboxForVideo() {
      let rows = this.$refs.videoTree.getCheckedNodes(true)
      this.playVideoBtnVisiable = rows.length > 0 ? true : false
    },
    // 点击通讯录节点复选框
    clickCheckbox() {
      let rows = this.$refs.userTree.getCheckedNodes(true)
      if (rows.length === 0) {
        this.callBtnVisiable = false
        this.intercomBtnVisiable = false
        this.smsBtnVisiable = false
        this.audioBtnVisiable = false
        this.videoBtnVisiable = false
        this.cameraBtnVisiable = false
        return
      }
      if (rows.length === 1) {
        this.audioBtnVisiable = true
        this.videoBtnVisiable = true
        if (rows[0].obj.videoID && rows[0].obj.videoID.length > 0) {
          this.cameraBtnVisiable = true
        } else this.cameraBtnVisiable = false
      } else {
        this.audioBtnVisiable = false
        this.videoBtnVisiable = false
        this.cameraBtnVisiable = false
      }
      this.callBtnVisiable = true

      let smsBtnVisiable = false,
        intercomBtnVisiable = false
      for (let i = 0; i < rows.length; i++) {
        const item = rows[i]
        if (item.type === 'employee') {
          // 职员
          let deviceType = item.obj.deviceType
          if (
            !intercomBtnVisiable &&
            (deviceType === 'poc' || deviceType === 'esip')
          ) {
            let telstatus = this.deviceStatusMap.get(item.obj.defaultNo)
            if (telstatus !== 'offline') {
              intercomBtnVisiable = true
            }
          }
          if (!smsBtnVisiable && this.hasSms(item)) smsBtnVisiable = true
        } else {
          smsBtnVisiable = true
        }
        if (smsBtnVisiable && intercomBtnVisiable) break
      }
      this.intercomBtnVisiable = intercomBtnVisiable
      this.smsBtnVisiable = smsBtnVisiable
    },
    // 节点选中状态改变
    checkChange(data, checked) {
      // console.log('checkChange', data, checked)
      switch (data.type) {
        case 'group':
        case 'operator':
        case 'employee':
          this.clickCheckbox()
          break
        case 'videogroup':
        case 'video':
          this.clickCheckboxForVideo()
          break
        default:
          break
      }
      if (data.children) return

      if (data.obj.pos_x && data.obj.pos_y) {
        let marker = this.getMarkerByNodeAndSetZIndex(data)
        if (checked) {
          if (!marker) {
            let point = new BMap.Point(data.obj.pos_x, data.obj.pos_y)
            marker = this.addMarker(data, point)
          } else marker.setZIndex(1)
          if (data.type === 'watcher') this.overlayInWatcher(data)
        }
      }
    },
    // 删除标记
    removeMarker(marker, type, id) {
      CustomInfoBox.closeById(id)
      this.defaultOverlayMap.delete(id)
      switch (type) {
        case 'vidoe':
          this.videoMarkerCluster &&
            this.videoMarkerCluster.removeMarker(marker)
          break
        case 'label':
          this.suppliesMarkerCluster &&
            this.suppliesMarkerCluster.removeMarker(marker)
          break
        case 'event':
          this.eventMarkerCluster &&
            this.eventMarkerCluster.removeMarker(marker)
          break
        case 'warning':
          this.warningMarkerCluster &&
            this.warningMarkerCluster.removeMarker(marker)
          break
        default:
          this.userMarkerCluster && this.userMarkerCluster.removeMarker(marker)
          break
      }
    },
    // 删除标记
    removeMarkerByNode(node) {
      let marker = this.defaultOverlayMap.get(node.id)
      if (marker) this.removeMarker(marker, node.type, node.id)
      let ref
      switch (node.type) {
        case 'vidoe':
          ref = 'videoTree'
          break
        case 'label':
          ref = 'suppliesTree'
          break
        case 'event':
          ref = 'eventTree'
          break
        case 'warning':
          ref = 'warningTree'
          break
        default:
          ref = 'userTree'
          break
      }
      let tree = this.$refs[ref]
      if (tree) {
        tree.setChecked(node.nid, false)
      }
    },
    // 获取点聚合
    getCluster(marker, type) {
      let clusters
      switch (type) {
        case 'vidoe':
          this.videoMarkerCluster &&
            (clusters = this.userMarkerCluster.getClusters())
          break
        case 'label':
          this.suppliesMarkerCluster &&
            (clusters = this.suppliesMarkerCluster.getClusters())
          break
        case 'event':
          this.eventMarkerCluster &&
            (clusters = this.eventMarkerCluster.getClusters())
          break
        case 'warning':
          this.warningMarkerCluster &&
            (clusters = this.warningMarkerCluster.getClusters())
          break
        default:
          this.userMarkerCluster &&
            (clusters = this.userMarkerCluster.getClusters())
          break
      }
      if (!clusters) return
      return clusters.find((c) => c.isMarkerInCluster(marker))
    },

    // 获取覆盖物
    getMarkerByNodeAndSetZIndex(node) {
      if (!this.defaultOverlayMap.get(node.id)) return
      let markers
      switch (node.type) {
        case 'vidoe':
          this.videoMarkerCluster &&
            (markers = this.userMarkerCluster.getMarkers())
          break
        case 'label':
          this.suppliesMarkerCluster &&
            (markers = this.suppliesMarkerCluster.getMarkers())
          break
        case 'event':
          this.eventMarkerCluster &&
            (markers = this.eventMarkerCluster.getMarkers())
          break
        case 'warning':
          this.warningMarkerCluster &&
            (markers = this.warningMarkerCluster.getMarkers())
          break
        default:
          this.userMarkerCluster &&
            (markers = this.userMarkerCluster.getMarkers())
          break
      }
      if (!markers) return
      let marker = markers.find((item) => item.data.id === node.id)
      return marker
    },
    // 获取ICON
    getIcon() {
      return 'static/bmap/images/location.png'
    },
    // 打开信息窗口
    openInfoBox(node, marker, cluster) {
      let infoBox, associatedObj
      switch (node.type) {
        case 'label':
          infoBox = this.createLabelInfoBox(node)
          break
        case 'video':
          associatedObj = this.associatedMap.get(node.obj.videoID)
          if (associatedObj) {
            infoBox = this.createSensorInfoBox(node, associatedObj)
            break
          } else {
            infoBox = this.createVideoInfoBox(node)
          }
          break
        case 'warning':
          infoBox = this.createWarningInfoBox(node)
          break
        case 'operator':
          infoBox = this.createOperatorInfoBox(node)
          break
        case 'employee':
          infoBox = this.createEmployeeInfoBox(node)
          break
        case 'event':
          infoBox = this.createEventInfoBox(node)
          break
        default:
          break
      }
      marker.infoBox = infoBox
      cluster && cluster.setInfoBox(infoBox)
      cluster ? infoBox.open(cluster) : infoBox.open(marker)
    },

    // 添加标注点
    addMarker(node, point, animation = false) {
      this.initMarkerCluster()

      // let marker = this.getMarkerByNodeAndSetZIndex(node)
      // if (marker) return marker

      // let data = node.obj
      let status = this.deviceStatus(node)
      let imgurl = this.getIcon()

      let size = this.scalePx(30)
      let large = this.scalePx(40)

      let icon = new BMap.Icon(imgurl, new BMap.Size(size, size), {
        anchor: new BMap.Size(size / 2, size),
        imageSize: new BMap.Size(size, size),
        infoWindowAnchor: new BMap.Size(size / 2, -5),
      })

      let myStyles = [
        {
          url: imgurl,
          size: new BMap.Size(size, size),
          textColor: '#ff00ff',
        },
      ]

      const self = this
      let className = `custom-text-icon ${node.type}`
      if (node.type === 'event') {
        className += ` level-${node.obj.eventType}`
      }
      let marker = new CustomTextIconOverlay(
        this.map,
        { point, icon, data: node },
        {
          animation: animation,
          className: className,
          styles: myStyles,
          anchor: new BMap.Size(0, -size / 2),
        }
      )

      marker.addEventListener('click', (e) => {
        self.map.panTo(e.target.getPosition())
        self.openInfoBox(node, marker)
      })
      // marker.addEventListener('mouseover', () => {
      //   let icon = marker.getIcon()
      //   icon.setSize(new BMap.Size(large, large))
      //   icon.setImageSize(new BMap.Size(large, large))
      //   icon.setAnchor(new BMap.Size(large / 2, large))
      //   icon.setInfoWindowAnchor(new BMap.Size(large / 2, -5))
      //   marker.setIcon(icon)
      //   marker.setSize(new BMap.Size(large, large))
      //   marker.setAnchor(new BMap.Size(0, -large / 2))
      //   self.openInfoBox(node, marker)
      // })
      // marker.addEventListener('mouseout', () => {
      //   let icon = marker.getIcon()
      //   icon.setSize(new BMap.Size(size, size))
      //   icon.setImageSize(new BMap.Size(size, size))
      //   icon.setAnchor(new BMap.Size(size / 2, size))
      //   icon.setInfoWindowAnchor(new BMap.Size(size / 2, -5))
      //   marker.setIcon(icon)
      //   marker.setSize(new BMap.Size(size, size))
      //   marker.setAnchor(new BMap.Size(0, -size / 2))
      //   if (marker.infoBox) {
      //     marker.infoBox.open(marker)
      //   }
      // })

      this.$nextTick(() => {
        switch (node.type) {
          case 'video':
            this.videoMarkerCluster.addMarker(marker)
            break
          case 'operator':
          case 'employee':
            this.userMarkerCluster.addMarker(marker)
            break
          case 'warning':
            this.warningMarkerCluster.addMarker(marker)
            break
          case 'label':
            this.suppliesMarkerCluster.addMarker(marker)
            break
          case 'event':
            this.eventMarkerCluster.addMarker(marker)
            break
        }
      })
      this.defaultOverlayMap.set(node.id, marker)
      marker.setZIndex(1)
      return marker
    },
    // 根据号码获取用户状态
    deviceStatus(node) {
      let tel
      switch (node.type) {
        case 'operator':
          tel = node.obj.mainTel || node.obj.viceTel || ''
          break
        case 'employee':
          tel = node.obj.defaultNo || ''
          break
        case 'video':
          return node.obj.status || 'st_online'
          break
        default:
          break
      }
      if (tel) {
        let status = this.deviceStatusMap.get(tel)
        return status ? status : 'online'
      }
      return 'online'
    },

    // 计算点是否在覆盖物内
    isPointInOverlay(point, e) {
      if (!e) e = this.drawingOverlayEvent
      if (!e) return false
      switch (e.drawingMode) {
        case BMAP_DRAWING_RECTANGLE:
          return BMapLib.GeoUtils.isPointInRect(point, e.overlay.getBounds())
        case BMAP_DRAWING_CIRCLE:
          return BMapLib.GeoUtils.isPointInCircle(point, e.overlay)
        case BMAP_DRAWING_POLYGON:
          return BMapLib.GeoUtils.isPointInPolygon(point, e.overlay)
        default:
          return false
      }
    },

    // 详情
    handleDetail(node) {
      if (node.type === 'label') {
        getSupplies(node.obj.suppliesId)
          .then((res) => {
            // console.log('定位标签', JSON.stringify(res));
            res.type = node.obj.type
            res.typeName = node.obj.typeName
            res.pos_x = node.obj.pos_x
            res.pos_y = node.obj.pos_y
            node.obj = res
            this.suppliesForm = deepClone(res)
            this.suppliesFormTitle = '物资详情'
            this.openSuppliesForm = true
          })
          .catch((err) => {
            console.log('getSupplies', err)
          })
      } else if (node.type === 'video') {
        // 摄像头
        this.cameraFormTitle =
          node.obj.videoMode === '5' ? '蹲点仪详情' : '摄像头详情'
        this.cameraForm = deepClone(node.obj)
        this.openCameraForm = true
      } else if (node.type === 'employee') {
        // 职员
        this.employeeFormTitle = '职员详情'
        this.employeeForm = deepClone(node.obj)
        this.openEmployeeForm = true
      } else if (node.type === 'operator') {
        // 操作员
        this.operatorFormTitle = '操作员详情'
        this.operatorForm = deepClone(node.obj)
        this.openOperatorForm = true
      } else if (node.type === 'event') {
        // 事件
        getEvent(node.id)
          .then((res) => {
            res.type = node.obj.type
            res.typeName = node.obj.typeName
            res.pos_x = res.eventLongitude
            res.pos_y = res.eventLatitude
            node.obj = res.data
            this.eventFormTitle = '事件详情'
            this.eventForm = deepClone(res.data)
            this.openEventForm = true
            console.log('getEvent', JSON.stringify(res.data))
          })
          .catch((err) => {
            console.log('getEvent', err)
          })
      }
    },
    // 初始化地图
    initBMap() {
      let map
      try {
        map = this.map = new BMap.Map('allmap', {
          maxZoom: bmapConfig.maxZoom || 19,
          minZoom: bmapConfig.minZoom || 4,
          enableMapClick: false,
        })
      } catch (error) {
        if (bmapConfig.useOnlineMap === true)
          msgError('在线地图加载失败，请检查地图秘钥或网络')
        return
      }

      this.SetCenterAndZoom()
      let center = new BMap.Point(
        this.centerAndZoom.lng,
        this.centerAndZoom.lat
      )
      map.addEventListener('load', () => {
        console.log('map.load 地图加载完成，图层未完成，开始初始化数据')
        this.initDrawingManager(map)
        this.initMarkerCluster()
        // 初始化标记点
        this.initDefaultOverlayByTypes(this.showOverlayTypes)

        // let marker = new BMap.Marker(center)
        // map.addOverlay(marker)
      })
      // 右键事件
      map.addEventListener('rightclick', this.rightclickEvent)
      map.centerAndZoom(center, this.centerAndZoom.zoom)
      //启用滚轮放大缩小
      map.enableScrollWheelZoom()
      //添加地图类型控件
      map.addControl(
        new BMap.MapTypeControl({
          mapTypes: [BMAP_NORMAL_MAP, BMAP_SATELLITE_MAP, BMAP_HYBRID_MAP],
        })
      )
      // 左下角，添加比例尺
      map.addControl(
        new BMap.ScaleControl({
          anchor: BMAP_ANCHOR_BOTTOM_LEFT,
        })
      )
      //右下角，添加默认缩放平移控件
      map.addControl(
        new BMap.NavigationControl({
          anchor: BMAP_ANCHOR_BOTTOM_RIGHT,
        })
      )
      if (bmapConfig.useOnlineMap === true) {
        var trafficControl = new BMapLib.TrafficControl({
          showPanel: false, //是否显示路况提示面板
        })
        map.addControl(trafficControl)
        trafficControl.setAnchor(BMAP_ANCHOR_BOTTOM_RIGHT)
      }
    },
    // 初始化绘图工具
    initDrawingManager(map) {
      //实例化鼠标绘制工具
      try {
        let drawingManager = (this.drawingManager = new BMapLib.DrawingManager(
          map,
          {
            isOpen: false, //是否开启绘制模式
            enableCalculate: true, // 开启计算
            enableDrawingTool: false, //是否显示工具栏
            drawingToolOptions: {
              anchor: BMAP_ANCHOR_TOP_RIGHT, //位置
              offset: new BMap.Size(95, 5), //偏离值
            },
            scale: 0.8, //工具栏缩放比例
            drawingModes: [
              //BMAP_DRAWING_MARKER,
              BMAP_DRAWING_CIRCLE,
              BMAP_DRAWING_POLYLINE,
              BMAP_DRAWING_POLYGON,
              BMAP_DRAWING_RECTANGLE,
              // BMAP_CUSTOM_TOOLS,
            ],
            circleOptions: this.styleOptions, //圆的样式
            polylineOptions: this.styleOptions, //线的样式
            polygonOptions: this.styleOptions, //多边形的样式
            rectangleOptions: this.styleOptions, //矩形的样式
          }
        ))

        //添加鼠标绘制工具监听事件，用于获取绘制结果
        drawingManager.addEventListener('overlaycomplete', this.overlaycomplete)
      } catch (error) {
        $.getScript('static/bmap/library/DrawingManager.js', () => {
          this.initDrawingManager(map)
          console.log('重新绘制工具初始化', error)
        })
      }
    },
    // 判断绘图工具是否初始化，未初始化则初始化
    isDrawingManagerInited() {
      if (!this.drawingManager) this.initDrawingManager(this.map)
    },
    //e:回调函数返回的event参数，包括：{"drawingMode":当前绘制模式,"overlay":对应模式的覆盖物,"calculate":面积,"label":label对象}
    overlaycomplete(e) {
      this.toolCommond = 'hander'

      switch (e.drawingMode) {
        case BMAP_DRAWING_MARKER: // 标注
          break
        case BMAP_CUSTOM_ICON: // 图标按钮
          this.handleIcon()
          return
        case BMAP_CUSTOM_REFRESH: // 刷新按钮
          this.handleRefresh()
          return
        case BMAP_CUSTOM_CENTER: // 设置中心点按钮
          this.handleCenterAndZoom()
          return
        case BMAP_CUSTOM_FULLSCREEN: // 全屏按钮
          this.handleFullScreen()
          return
        case BMAP_CUSTOM_CIRCLE_AREA: // 圆面积按钮
        case BMAP_CUSTOM_POLYGON_AREA: // 矩形面积按钮
        case BMAP_CUSTOM_RECTANGLE_AREA: // 多边面积按钮
          this.completeArea(e)
          return
        default:
          // 面积不存在不显示
          if (!e.calculate) {
            this.map.removeOverlay(e.overlay)
            return
          }
          break
      }
      // 标注
      if (e.drawingMode == BMAP_DRAWING_MARKER) {
        this.map.removeOverlay(e.overlay)
        let point = e.overlay.getPosition()
        // this.$refs.tree.getCurrentNode().unmarked = false;
        this.currentNodeData.obj.pos_x = point.lng
        this.currentNodeData.obj.pos_y = point.lat
        this.addMarker(this.currentNodeData, point)
        e.target.close()
        this.infoModify(this.currentNodeData)
        return
      }

      if (e.drawingMode == 'fence') {
        this.handleFence(e.overlay)
        return
      }

      this.clearAllOverlay()

      //获取覆盖物范围内的标注
      if (BMapLib.GeoUtils) {
        this.map.addOverlay(e.overlay)
        // 添加事件
        // e.overlay.addEventListener('click', () => {
        //   msgError("aaaa")
        // })
        this.drawingOverlay = e.overlay
        this.drawingOverlayEvent = e
        this.isDrawingResult = true

        this.resetTree(e)

        e.target.close()
        this.showTrack = false
        this.showContent = true
        this.showTree = true
        this.showResult = true
      }
    },
    // 面积测量完成
    completeArea(e) {
      this.drawingManager.close()
      // 面积不存在不显示
      if (!e.calculate) {
        this.map.removeOverlay(e.overlay)
        return
      }
      console.error('面积测量完成', e.calculate)
      let point
      switch (e.drawingMode) {
        case BMAP_CUSTOM_CIRCLE_AREA: // 圆面积按钮
          point = e.overlay.getCenter()
          break
        case BMAP_CUSTOM_POLYGON_AREA: // 矩形面积按钮
        case BMAP_CUSTOM_RECTANGLE_AREA: // 多边面积按钮
          let points = e.overlay.getPath()
          point = points[points.length - 1]
          break
      }

      let label = new BMap.Label('区域面积：' + e.calculate + 'm3')
      label.setPosition(point)
      this.map.addOverlay(label)
      // 添加关闭按钮
      var bico = new BMap.Icon(
        'static/bmap/images/mapctrls.gif',
        new BMap.Size(12, 12),
        { imageOffset: new BMap.Size(0, -14) }
      )
      let closeMaker = new BMap.Marker(point, {
        icon: bico,
        offset: new BMap.Size(-10, 10),
        baseZIndex: 3600000,
      })
      const remove = () => {
        this.map.removeOverlay(e.overlay)
        this.map.removeOverlay(label)
        this.map.removeOverlay(closeMaker)
        closeMaker.removeEventListener('click', remove)
      }
      this.map.addOverlay(closeMaker)
      closeMaker.setTitle('清除本次测距')
      closeMaker.addEventListener('click', remove)
    },
    // 清除所有覆盖物
    clearAllOverlay() {
      this.clearAllClusterMarkers()
      this.defaultOverlayMap.clear()
    },
    // 地图上添加电子围栏
    addFenceOverlay(fenceData) {
      console.log('地图上添加电子围栏', fenceData)
      fenceData.points = fenceData.points.map((item) => {
        return new BMap.Point(item.pos_x, item.pos_y)
      })
      let fence = new CustomFence(fenceData)
      // 添加电子围栏覆盖物
      fence.addFence(this.map, { size: this.scalePx(40) }, this.clickFenceFlag)
    },
    // 处理电子围栏
    handleFence(polygon) {
      this.map.removeOverlay(polygon)
      let points = polygon.getPath()
      if (points.length < 3) return
      let users = []

      for (const item of this.employeeMap.values()) {
        if (
          item.pos_x &&
          item.pos_y &&
          BMapLib.GeoUtils.isPointInPolygon(
            new BMap.Point(item.pos_x, item.pos_y),
            polygon
          )
        ) {
          console.log('在电子围栏中', item.realName)
          users.push({
            userID: item.employeeID,
            userName: item.realName,
            userTel: item.defaultNo,
          })
        }
      }
      console.log('handleFence', points, users)
      let fence = new CustomFence({ points, users })
      // 添加电子围栏覆盖物
      fence.addFence(this.map, { size: this.scalePx(40) }, this.clickFenceFlag)
    },
    // 保存电子围栏数据处理
    veriFence(fence) {
      console.log('保存电子围栏', fence)
      if (!fence.fenceName) {
        msgError('围栏名称不能为空')
        return null
      }
      let points = fence.points.map((item) => {
        return { pos_x: item.lng, pos_y: item.lat }
      })
      return {
        fenceName: fence.fenceName,
        fenceID: fence.fenceID,
        points,
        users: fence.users,
      }
    },
    // 删除电子围栏
    deleteFence(fence) {
      if (!fence.fenceID) {
        fence.removeFence()
        return
      }
      SDK.deleteFence(fence.fenceID)
        .then((res) => {
          console.log('删除电子围栏成功', res)
          fence.removeFence()
        })
        .catch((err) => {
          console.error('删除电子围栏失败', err)
          msgError('删除电子围栏失败')
        })
    },
    // 点击电子围栏旗子
    clickFenceFlag(fence) {
      let userNames = fence.users.map((item) => item.userName).join(', ')

      let dom = this.createDom('div', 'custom-info-box fence')
      this.appendHeader(dom, 'title', '电子围栏')
      // 内容
      let body = this.createDom('div', 'fence_body')

      // 第一行
      let flex1 = this.createDom('div', 'flex')
      let label = this.createDom(
        'label',
        '',
        '<span style="color:red;">*</span>名称：'
      )
      flex1.appendChild(label)
      let input = this.createDom('input', '', {
        type: 'text',
        value: fence.fenceName || '',
      })
      let name = this.createDom('span', '', fence.fenceName || '')
      fence.fenceID ? flex1.appendChild(name) : flex1.appendChild(input)

      body.appendChild(flex1)

      // 第二行
      let flex2 = this.createDom('div', 'flex')
      let label2 = this.createDom('label', '', '人员：')
      flex2.appendChild(label2)
      // 人员
      let users = this.createDom('div', 'users', userNames || '')
      // 添加人员按钮
      let btn = this.createDom('button', '', '添加')
      btn.addEventListener('click', () => {
        this.addFenceUser(fence, (userRows) => {
          fence.users = userRows
          userNames = userRows.map((item) => item.userName).join(', ')
          users.innerHTML = userNames || ''
        })
      })

      // 第三行
      let flex3 = this.createDom('div', 'flex')
      let label3 = this.createDom('label')

      if (fence.fenceID) {
        flex2.appendChild(users)
        body.appendChild(flex2)
      } else {
        flex2.appendChild(btn)
        // 第三行
        flex3.append(label3, users)
        body.append(flex2, flex3)
      }

      // 底部
      let footer = this.createDom('div', 'info-box-footer fence_footer')
      let delbtn = this.createDom('button', '', '删除')
      delbtn.addEventListener('click', () => {
        this.deleteFence(fence)
      })

      // 保存按钮
      let savebtn = this.createDom('button', '', '保存')
      // 编辑按钮
      let editbtn = this.createDom('button', '', '编辑')
      // 历史告警按钮
      let alarmbtn = this.createDom('button', '', '报警')

      savebtn.addEventListener('click', () => {
        fence.fenceName = input.value
        let form = this.veriFence(fence)
        if (!form) return
        SDK.editfence(form)
          .then((res) => {
            msgSuccess('保存电子围栏成功')
            // 新增返回id
            if (res.fenceID) fence.fenceID = res.fenceID
            name.innerHTML = fence.fenceName
            flex1.replaceChild(name, input)
            body.removeChild(flex3)
            flex2.replaceChild(users, btn)
            footer.replaceChild(editbtn, savebtn)
            footer.insertBefore(alarmbtn, editbtn)
          })
          .catch((err) => {
            console.error('保存电子围栏失败', err)
          })
      })
      editbtn.addEventListener('click', () => {
        flex1.replaceChild(input, name)
        flex2.replaceChild(btn, users)
        flex3.append(label3, users)
        body.appendChild(flex3)
        footer.removeChild(alarmbtn)
        footer.replaceChild(savebtn, editbtn)
      })
      alarmbtn.addEventListener('click', () => {
        this.viewFenceAlarm(fence)
      })
      if (fence.fenceID) {
        footer.append(editbtn, alarmbtn)
      } else {
        footer.appendChild(savebtn)
      }

      footer.appendChild(delbtn)

      dom.append(body, footer)
      this.appendArrow(dom)

      let infoBox = new CustomInfoBox(this.map, fence.fenceID, dom, {
        closeIconClick: () => fence.setInfoBox(null),
      })

      fence.setInfoBox(infoBox)
      infoBox.open(fence.marker)
    },
    // 创建告警信息窗口
    createWarningInfoBox(node) {
      let dom = this.createDom('div', 'custom-info-box warning')
      this.appendHeader(
        dom,
        'title',
        `${node.obj.equipmentName}(${node.label})`
      )
      // 内容
      let posX = this.formatStr(node.obj.pos_x)
      let posY = this.formatStr(node.obj.pos_y)
      let body = this.createDom(
        'div',
        'warning_body',
        `<div class='flex'><label>经度: </label><span>${posX}</span></div>
         <div class='flex'><label>纬度: </label><span>${posY}</span></div>`
      )
      // 底部
      let footer = this.createDom('div', 'info-box-footer warning_footer')
      // 告警按钮
      let alarmbtn = this.createDom('button', '', '告警')

      alarmbtn.addEventListener('click', () => {
        this.handleWarning(node.id)
      })
      footer.appendChild(alarmbtn)
      // 打开摄像头
      if (node.obj.videoId !== '') {
        let alarmVideobtn = this.createDom('button', '', '摄像头')
        let videoIds = node.obj.videoId.split(',')
        alarmVideobtn.addEventListener('click', () => {
          this.playBindingVideo(videoIds)
        })
        footer.appendChild(alarmVideobtn)
      }
      dom.append(body, footer)
      this.appendArrow(dom)
      return new CustomInfoBox(this.map, node.id, dom)
    },
    // 创建操作员信息窗口
    createOperatorInfoBox(node) {
      let dom = this.createDom('div', 'custom-info-box operator')
      this.appendHeader(dom, 'title', node.label)
      // 内容
      let posX = this.formatStr(node.obj.pos_x)
      let posY = this.formatStr(node.obj.pos_y)
      let groupName = this.getGroupName(node) || ''
      let mainTel = node.obj.mainTel
      let viceTel = node.obj.viceTel
      let innerHTML = `<div class='flex'><label>组：</label><span>${groupName}</span></div>`
      if (mainTel && viceTel)
        innerHTML += `<div class='flex'><label>手柄：</label><span>${mainTel}&nbsp;&nbsp;${viceTel}</span></div>`
      else if (mainTel)
        innerHTML += `<div class='flex'><label>左：</label><span>${mainTel}</span></div>`
      else if (viceTel)
        innerHTML += `<div class='flex'><label>右：</label><span>${viceTel}</span></div>`
      innerHTML += `<div class='flex'><label>位置：</label><span>${posX}&nbsp;&nbsp;${posY}</span></div>`
      let body = this.createDom('div', 'operator_body', innerHTML)
      // 底部
      let footer = this.createDom('div', 'info-box-footer operator_footer')
      // 语音按钮
      let audiobtn = this.createDom('button', '', '语音')
      audiobtn.addEventListener('click', () => {
        this.makeCall(node, 'audio')
      })
      // 视频按钮
      let videobtn = this.createDom('button', '', '视频')
      videobtn.addEventListener('click', () => {
        this.makeCall(node, 'video')
      })
      // 短信按钮
      let smsbtn = this.createDom('button', '', '短信')
      smsbtn.addEventListener('click', () => {
        this.handleSms(node)
      })
      // 查视频按钮
      let viewbtn = this.createDom('button', '', '监控')
      viewbtn.addEventListener('click', () => {
        this.playBindingVideo(node.obj.videoID)
      })
      // 移除按钮
      let delbtn = this.createDom('button', '', '移除')
      delbtn.addEventListener('click', () => {
        this.handleRemove(node)
      })
      // 详情按钮
      let detailbtn = this.createDom('button', '', '详情')
      detailbtn.addEventListener('click', () => {
        this.handleDetail(node)
      })
      // 查轨迹按钮
      let trackbtn = this.createDom('button', '', '轨迹')
      trackbtn.addEventListener('click', () => {
        this.handleTrack(node)
      })
      footer.append(audiobtn, videobtn, smsbtn)
      if (node.obj.videoID && node.obj.videoID.length > 0)
        footer.appendChild(viewbtn)
      if (node.obj.gpsBH) footer.appendChild(trackbtn)
      footer.append(delbtn, detailbtn)
      dom.append(body, footer)
      this.appendArrow(dom)
      return new CustomInfoBox(this.map, node.id, dom)
    },
    // 创建职员信息窗口
    createEmployeeInfoBox(node) {
      let dom = this.createDom('div', 'custom-info-box employee')
      this.appendHeader(dom, 'title', node.label)
      // 内容
      let posX = this.formatStr(node.obj.pos_x)
      let posY = this.formatStr(node.obj.pos_y)
      let groupName = this.getGroupName(node) || ''
      let tel = node.obj.defaultNo
      let innerHTML = `<div class='flex'><label>组：</label><span>${groupName}</span></div>`
      innerHTML += `<div class='flex'><label>号码：</label><span>${tel}</span></div>`
      innerHTML += `<div class='flex'><label>位置：</label><span>${posX}&nbsp;&nbsp;${posY}</span></div>`

      let body = this.createDom('div', 'employee_body', innerHTML)
      // 底部
      let footer = this.createDom('div', 'info-box-footer employee_footer')
      // 语音按钮
      let audiobtn = this.createDom('button', '', '语音')
      audiobtn.addEventListener('click', () => {
        this.makeCall(node, 'audio')
      })
      // 视频按钮
      let videobtn = this.createDom('button', '', '视频')
      videobtn.addEventListener('click', () => {
        this.makeCall(node, 'video')
      })
      // 对讲按钮
      let halfbtn = this.createDom('button', '', '对讲')
      halfbtn.addEventListener('click', () => {
        this.makeCall(node, 'half-audio')
      })
      // 短信按钮
      let smsbtn = this.createDom('button', '', '短信')
      smsbtn.addEventListener('click', () => {
        this.handleSms(node)
      })
      // 查视频按钮
      let viewbtn = this.createDom('button', '', '监控')
      viewbtn.addEventListener('click', () => {
        this.playBindingVideo(node.obj.videoID)
      })
      // 移除按钮
      let delbtn = this.createDom('button', '', '移除')
      delbtn.addEventListener('click', () => {
        this.handleRemove(node)
      })
      // 详情按钮
      let detailbtn = this.createDom('button', '', '详情')
      detailbtn.addEventListener('click', () => {
        this.handleDetail(node)
      })
      // 查轨迹按钮
      let trackbtn = this.createDom('button', '', '轨迹')
      trackbtn.addEventListener('click', () => {
        this.handleTrack(node)
      })
      footer.append(audiobtn, videobtn)
      if (this.handMicphone && node.obj.deviceType == 'poc')
        footer.appendChild(halfbtn)
      if (this.hasSms(node)) footer.appendChild(smsbtn)
      if (node.obj.videoID && node.obj.videoID.length > 0)
        footer.appendChild(viewbtn)
      if (node.obj.gpsBH) footer.appendChild(trackbtn)
      footer.append(delbtn, detailbtn)

      dom.append(body, footer)
      this.appendArrow(dom)
      return new CustomInfoBox(this.map, node.id, dom)
    },
    // 创建摄像头信息窗口
    createVideoInfoBox(node) {
      let dom = this.createDom('div', 'custom-info-box video')
      this.appendHeader(dom, 'title', node.label)
      // 内容
      let posX = this.formatStr(node.obj.pos_x)
      let posY = this.formatStr(node.obj.pos_y)
      let groupName = this.getGroupName(node) || ''
      let body = this.createDom(
        'div',
        'video_body',
        `<div class='flex'><label>组：</label><span>${groupName}</span></div>
         <div class='flex'><label>经度：</label><span>${posX}</span></div>
         <div class='flex'><label>纬度：</label><span>${posY}</span></div>`
      )
      // 底部
      let footer = this.createDom('div', 'info-box-footer video_footer')
      // 详情按钮
      let detailbtn = this.createDom('button', '', '详情')
      detailbtn.addEventListener('click', () => {
        this.handleDetail(node)
      })
      // 查视频按钮
      let viewbtn = this.createDom('button', '', '监控')
      viewbtn.addEventListener('click', () => {
        this.playVideoBefore(node)
      })
      // 查轨迹按钮
      let trackbtn = this.createDom('button', '', '轨迹')
      trackbtn.addEventListener('click', () => {
        this.handleTrack(node)
      })
      // 移除按钮
      let delbtn = this.createDom('button', '', '移除')
      delbtn.addEventListener('click', () => {
        this.handleRemove(node)
      })

      node.obj.gpsBH
        ? footer.append(detailbtn, trackbtn, viewbtn, delbtn)
        : footer.append(detailbtn, viewbtn, delbtn)
      dom.append(body, footer)
      this.appendArrow(dom)
      return new CustomInfoBox(this.map, node.id, dom)
    },
    // 创建事件信息窗口
    createEventInfoBox(node) {
      let dom = this.createDom('div', 'custom-info-box event')
      let level = node.obj.eventType
      let statusLabel = selectDictLabel(this.eventTypeOptions, level)
      let titleInnerHTML = `${node.label}<span class='envent-level level-${level}'>(${statusLabel})</span>`
      this.appendHeader(dom, 'title', titleInnerHTML)
      // 内容
      let posX = this.formatStr(node.obj.pos_x)
      let posY = this.formatStr(node.obj.pos_y)
      let userName = node.obj.userName || ''
      let tel = node.obj.userTel
      let innerHTML = `<div class='flex'><label>上报：</label><span>${userName}</span></div>`
      innerHTML += `<div class='flex'><label>号码：</label><span>${tel}</span></div>`
      innerHTML += `<div class='flex'><label>位置：</label><span>${posX}&nbsp;&nbsp;${posY}</span></div>`

      let body = this.createDom('div', 'event_body', innerHTML)
      // 底部
      let footer = this.createDom('div', 'info-box-footer event_footer')
      // 语音按钮
      let audiobtn = this.createDom('button', '', '语音')
      audiobtn.addEventListener('click', () => {
        this.makeCallForEvent(node.obj.userTel, 'audio')
      })
      // 视频按钮
      let videobtn = this.createDom('button', '', '视频')
      videobtn.addEventListener('click', () => {
        this.makeCallForEvent(node.obj.userTel, 'video')
      })
      // 移除按钮
      let delbtn = this.createDom('button', '', '移除')
      delbtn.addEventListener('click', () => {
        this.handleRemove(node)
      })
      // 详情按钮
      let detailbtn = this.createDom('button', '', '详情')
      detailbtn.addEventListener('click', () => {
        this.handleDetail(node)
      })

      footer.append(audiobtn, videobtn, delbtn, detailbtn)

      dom.append(body, footer)
      this.appendArrow(dom)
      return new CustomInfoBox(this.map, node.id, dom)
    },
    // 添加箭头
    appendArrow(dom, size) {
      // 箭头
      let arrow = document.createElement('div')
      arrow.className = size ? `arrow_bottom_${size}` : 'arrow_bottom'
      dom.appendChild(arrow)
    },
    // 添加标题
    appendHeader(dom, className, innerHTML) {
      let header = this.createDom('div', 'info-box-header')
      let title = this.createDom('div', className || 'title', innerHTML)
      let close = this.createDom('img', 'close-icon', {
        src: 'static/bmap/images/close.png',
      })
      header.append(title, close)
      dom.appendChild(header)
    },
    // 创建dom元素
    createDom(tagName, className, attrs) {
      let dom = document.createElement(tagName || 'div')
      className ? (dom.className = className) : ''
      if (!attrs) return dom
      if (attrs.constructor === Object) {
        for (let key in attrs) {
          dom.setAttribute(key, attrs[key])
        }
        return dom
      }
      dom.innerHTML = attrs
      return dom
    },
    // 创建定位标签信息窗口
    createLabelInfoBox(node) {
      let dom = this.createDom('div', 'custom-info-box label')
      this.appendHeader(dom, 'title', node.label)
      let person
      let empty = false
      if (!node.obj.personList || node.obj.personList.length === 0) {
        person = {}
        empty = true
      } else person = node.obj.personList[0]

      let body = this.createDom('div', 'label_body')

      let left = this.createDom(
        'div',
        'left',
        `<img src='${person.pic || 'static/bmap/images/avatar.png'}' />`
      )
      let right = this.createDom('div', 'right')
      let items = ['姓名', '部门', '职位', '年龄', '号码']
      let props = ['personName', 'groupName', 'dutyName', 'age', 'personTel']
      items.forEach((item, index) => {
        let value = person[props[index]] || ''
        let row = this.createDom(
          'div',
          'right-item',
          `<div class='prop'>${item}:</div><div class='value'>${
            value || ''
          }</div>`
        )
        right.appendChild(row)
      })

      body.append(left, right)
      // 底部
      let footer = this.createDom('div', 'info-box-footer label_footer')
      // 语音按钮
      let audiobtn = this.createDom('button', '', '语音')
      audiobtn.addEventListener('click', () => {
        this.makeCallForSupplies(person, 'audio')
      })
      // 视频按钮
      let videobtn = this.createDom('button', '', '视频')
      videobtn.addEventListener('click', () => {
        this.makeCallForSupplies(person, 'video')
      })
      // 移除按钮
      let delbtn = this.createDom('button', '', '移除')
      delbtn.addEventListener('click', () => {
        this.handleRemove(node)
      })
      // 详情按钮
      let detailbtn = this.createDom('button', '', '详情')
      detailbtn.addEventListener('click', () => {
        this.handleDetail(node)
      })
      footer.append(audiobtn, videobtn, delbtn, detailbtn)

      empty ? dom.appendChild(body) : dom.append(body, footer)
      this.appendArrow(dom)
      return new CustomInfoBox(this.map, node.id, dom)
    },
    // 创建传感器信息窗口
    createSensorInfoBox(node, associatedObj) {
      let dom = this.createDom('div', 'custom-info-box sensor')
      this.appendHeader(
        dom,
        'title',
        `${node.label}(<span class='associate'>${associatedObj.suppliesName}</span>)`
      )
      let body = this.createDom('div', 'sensor_body')

      let items = ['温度', '湿度', '风速', '风向']
      let itemValues = ['temperature', 'humidity', 'windSpeed', 'windDirectMsg']
      let airItems = ['SO2', 'CH4', 'CO', 'H2S']
      let airDatas = [
        'First_airData',
        'Second_airData',
        'Third_airData',
        'Fourth_airData',
      ]
      let airStates = [
        'First_airState',
        'Second_airState',
        'Third_airState',
        'Fourth_airState',
      ]
      let isEmpty = associatedObj.realInfo ? false : true
      let leftCol = this.createDom('div', 'col-left')
      items.forEach((item, index) => {
        let value = isEmpty ? '' : associatedObj.realInfo[itemValues[index]]
        let row = this.createDom(
          'div',
          'row',
          `<div class='label'>${item}:</div><div class='value'>${value}</div>`
        )
        leftCol.appendChild(row)
      })
      let righttCol = this.createDom('div', 'col-right')
      airItems.forEach((item, index) => {
        let value = isEmpty ? '' : associatedObj.realInfo[airDatas[index]]
        let status = isEmpty ? '' : associatedObj.realInfo[airStates[index]]
        let statusClass = !status
          ? 'normal'
          : status.includes('高报')
          ? 'high-risk'
          : 'low-risk'
        let statusLabel = !status
          ? '正常'
          : status.includes('高报')
          ? '高报'
          : '低报'

        let row = this.createDom(
          'div',
          'row',
          `<div class='label'>SO2:</div><div class='value'>${value}</div><div class='status ${statusClass}'>${statusLabel}</div>`
        )
        righttCol.appendChild(row)
      })

      body.append(leftCol, righttCol)
      dom.appendChild(body)
      this.appendArrow(dom)
      return new CustomInfoBox(this.map, node.id, dom)
    },
    // 查看电子围栏报警历史记录
    viewFenceAlarm(fence) {
      console.log('查看电子围栏报警历史记录', fence)
      this.openFenceAlarm = true
      this.fenceAlarmFormData = fence
    },
    // 添加电子围栏人员
    addFenceUser(fence, callback) {
      console.log('添加电子围栏人员', fence)
      this.openFenceUser = true
      this.fenceUserFormData = fence
      this.fenceUserSave = callback
    },
    // 默认按钮-设置中心点地图和缩放等级
    handleCenterAndZoom() {
      if (this.map.getZoom() <= 0) {
        msgWarning(缩放等级不能为0)
        return
      }
      this.$confirm('确定将当前位置和缩放等级设置为默认值吗?', '', {
        customClass: 'custom-message-box',
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          let center = this.map.getCenter()
          let zoom = this.map.getZoom()
          center.zoom = zoom
          this.$store.commit('SetCenterAndZoom')
          msgSuccess('设置成功')
        })
        .catch(() => {})
    },
    // 刷新按钮
    handleRefresh() {
      this.initBMap()
    },
    // 全屏按钮
    handleFullScreen() {
      if (!screenfull.isEnabled) {
        msgWarning('浏览器不支持全屏')
        return false
      }
      // screenfull.on('change', () => {
      //   if (screenfull.isFullscreen) {

      //   } else {
      //   }
      // })
      let div = document.getElementById('allmap')
      screenfull.toggle(div)
    },

    /**
     * 初始化数据
     */
    initData() {
      this.$store.dispatch('InitSuppliesData')
      this.$store.dispatch('InitEventData')
    },
    // 实现移动端拖拽
    touchstart(event, elId) {
      console.log('touchstart', elId)
      event.preventDefault()
      let dragDom = document.getElementById(elId)
      let box = document.getElementById('map-container')
      //鼠标按下，计算当前元素距离可视区的距离
      let e = event.targetTouches[0]
      const disX = e.clientX
      const disY = e.clientY
      const dragDomWidth = dragDom.offsetWidth
      const dragDomheight = dragDom.offsetHeight
      const screenWidth = box.clientWidth
      const screenHeight = box.clientHeight
      const minDragDomLeft = dragDom.offsetLeft
      const maxDragDomLeft = screenWidth - dragDomWidth - dragDom.offsetLeft
      const minDragDomTop = dragDom.offsetTop
      const maxDragDomTop = screenHeight - dragDomheight - dragDom.offsetTop

      const sty = (function () {
        if (window.document.currentStyle) {
          return (dom, attr) => dom.currentStyle[attr]
        } else {
          return (dom, attr) => getComputedStyle(dom, false)[attr]
        }
      })()
      // 获取到的值带px 正则匹配替换
      let styL = sty(dragDom, 'left')
      let styT = sty(dragDom, 'top')
      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
      if (styL.includes('%')) {
        styL = +screenWidth * (+styL.replace(/\%/g, '') / 100)
        styT = +screenHeight * (+styT.replace(/\%/g, '') / 100)
      } else {
        styL = +styL.replace(/\px/g, '')
        styT = +styT.replace(/\px/g, '')
      }
      let touchDragObj = {
        disX,
        disY,
        minDragDomLeft,
        maxDragDomLeft,
        minDragDomTop,
        maxDragDomTop,
        styL,
        styT,
      }
      elId === 'webrtc-video'
        ? (this.touchDragObj4Webrtc = touchDragObj)
        : (this.touchDragObj = touchDragObj)
    },
    touchmove(event, elId) {
      console.log('touchmove', elId)
      event.preventDefault()
      let touchDragObj =
        elId === 'webrtc-video' ? this.touchDragObj4Webrtc : this.touchDragObj
      let e = event.targetTouches[0]
      //通过事件委托，计算移动的距离
      let left = e.clientX - touchDragObj.disX
      let top = e.clientY - touchDragObj.disY
      // 边界处理
      if (-left > touchDragObj.minDragDomLeft) {
        left = -touchDragObj.minDragDomLeft
      } else if (left > touchDragObj.maxDragDomLeft) {
        left = touchDragObj.maxDragDomLeft
      }

      if (-top > touchDragObj.minDragDomTop) {
        top = -touchDragObj.minDragDomTop
      } else if (top > touchDragObj.maxDragDomTop) {
        top = touchDragObj.maxDragDomTop
      }
      // 移动当前元素
      let dragDom = document.getElementById(elId)
      dragDom.style.cssText += `; left: ${left + touchDragObj.styL}px; top: ${
        top + touchDragObj.styT
      }px;`
    },
    // 位置订阅
    subscribeLocation() {
      SDK.subscribeLocation()
        .then((res) => {
          // console.log('位置订阅', res)
        })
        .catch((err) => console.log('位置订阅', err))
    },

    //初始化点聚合
    initMarkerCluster() {
      if (this.markerClusterInited) return
      let styles = [
        {
          size: new BMap.Size(this.scalePx(50), this.scalePx(50)),
          textSize: 16,
          url: '',
          textColor: '#ff00ff',
        },
      ]
      if (
        !this.videoMarkerCluster ||
        this.videoMarkerCluster.getMap() !== this.map
      )
        this.videoMarkerCluster = new BMapLib.MarkerClusterer(this.map, {
          className: 'cluster-text-icon video',
          styles: styles,
        })
      if (
        !this.userMarkerCluster ||
        this.userMarkerCluster.getMap() !== this.map
      )
        this.userMarkerCluster = new BMapLib.MarkerClusterer(this.map, {
          className: 'cluster-text-icon user',
          styles: styles,
        })

      if (this.hasEventModule) {
        if (
          !this.eventMarkerCluster ||
          this.eventMarkerCluster.getMap() !== this.map
        )
          this.eventMarkerCluster = new BMapLib.MarkerClusterer(this.map, {
            className: 'cluster-text-icon event',
            styles: styles,
          })
      }
      if (this.hasWarningModule) {
        if (
          !this.warningMarkerCluster ||
          this.warningMarkerCluster.getMap() !== this.map
        )
          this.warningMarkerCluster = new BMapLib.MarkerClusterer(this.map, {
            className: 'cluster-text-icon warning',
            styles: styles,
          })
      }
      if (this.hasSuppliesModule) {
        if (
          !this.suppliesMarkerCluster ||
          this.suppliesMarkerCluster.getMap() !== this.map
        )
          this.suppliesMarkerCluster = new BMapLib.MarkerClusterer(this.map, {
            className: 'cluster-text-icon label',
            styles: styles,
          })
      }

      this.markerClusterInited = true
    },
    // 初始化地图上标记
    initDefaultOverlay(type) {
      // if (this.isDrawingResult) return
      let nids, point
      this.$nextTick(() => {
        switch (type) {
          case 'supplies': // 物资
            // console.log('suppliesMap', JSON.stringify([...this.suppliesMap.values()]));
            if (this.suppliesMap && this.hasSuppliesModule) {
              for (const item of this.suppliesMap.values()) {
                if (item.type === 'sensor') continue
                if (
                  !this.defaultOverlayMap.get(item.suppliesId) &&
                  item.pos_x &&
                  item.pos_y
                ) {
                  point = new BMap.Point(item.pos_x, item.pos_y)
                  if (this.isDrawingResult && !this.isPointInOverlay(point)) {
                    continue
                  }
                  nids = this.id2nidMap.get(item.suppliesId)
                  this.addMarker(
                    {
                      type: item.type,
                      obj: item,
                      label: item.suppliesName,
                      id: item.suppliesId,
                      parentID: item.typeId,
                      nid: nids ? nids[0] : '',
                    },
                    point
                  )
                }
              }
            }
            break
          case 'video': // 摄像头
            if (this.videoMap) {
              for (const item of this.videoMap.values()) {
                if (
                  !this.defaultOverlayMap.get(item.videoID) &&
                  item.pos_x &&
                  item.pos_y
                ) {
                  point = new BMap.Point(item.pos_x, item.pos_y)
                  if (this.isDrawingResult && !this.isPointInOverlay(point)) {
                    continue
                  }
                  nids = this.id2nidMap.get(item.videoID)
                  this.addMarker(
                    {
                      type: 'video',
                      obj: item,
                      label: item.videoName,
                      id: item.videoID,
                      parentID: item.group[0].groupID,
                      nid: nids ? nids[0] : '',
                    },
                    point
                  )
                }
              }
            }
            break
          case 'employee': // 职员
            if (this.employeeMap) {
              for (const item of this.employeeMap.values()) {
                if (
                  !this.defaultOverlayMap.get(item.employeeID) &&
                  item.pos_x &&
                  item.pos_y
                ) {
                  point = new BMap.Point(item.pos_x, item.pos_y)
                  if (this.isDrawingResult && !this.isPointInOverlay(point)) {
                    continue
                  }
                  nids = this.id2nidMap.get(item.videoID)
                  this.addMarker(
                    {
                      type: 'employee',
                      obj: item,
                      label: item.realName,
                      id: item.employeeID,
                      parentID: item.group[0].groupID,
                      nid: nids ? nids[0] : '',
                    },
                    point
                  )
                }
              }
            }
            break
          case 'operator': // 职员
            if (this.operatorMap) {
              for (const item of this.operatorMap.values()) {
                if (
                  !this.defaultOverlayMap.get(item.operatorID) &&
                  item.pos_x &&
                  item.pos_y
                ) {
                  point = new BMap.Point(item.pos_x, item.pos_y)
                  if (this.isDrawingResult && !this.isPointInOverlay(point)) {
                    continue
                  }
                  nids = this.id2nidMap.get(item.videoID)
                  this.addMarker(
                    {
                      type: 'operator',
                      obj: item,
                      label: item.realName,
                      id: item.operatorID,
                      parentID: item.groupID,
                      nid: nids ? nids[0] : '',
                    },
                    point
                  )
                }
              }
            }
            break
          case 'event': // 事件
            if (this.eventMap && this.hasEventModule) {
              for (const item of this.eventMap.values()) {
                if (
                  !this.defaultOverlayMap.get(item.eventId) &&
                  item.pos_x &&
                  item.pos_y
                ) {
                  point = new BMap.Point(item.pos_x, item.pos_y)
                  if (this.isDrawingResult && !this.isPointInOverlay(point)) {
                    continue
                  }
                  nids = this.id2nidMap.get(item.eventId)
                  this.addMarker(
                    {
                      type: 'event',
                      obj: item,
                      label: item.eventName,
                      id: item.eventId,
                      parentID: item.eventType,
                      nid: nids ? nids[0] : '',
                    },
                    point,
                    true
                  )
                }
              }
            }
            break
          case 'warning': // 告警
            if (type)
              if (this.warningEquipmentPartMap && this.hasWarningModule) {
                for (const item of this.warningEquipmentPartMap.values()) {
                  if (
                    !this.defaultOverlayMap.get(item.partId) &&
                    item.pos_x &&
                    item.pos_y
                  ) {
                    point = new BMap.Point(item.pos_x, item.pos_y)
                    if (this.isDrawingResult && !this.isPointInOverlay(point)) {
                      continue
                    }
                    nids = this.id2nidMap.get(item.partId)
                    this.addMarker(
                      {
                        type: 'warning',
                        obj: item,
                        label: item.partName,
                        id: item.partId,
                        parentID: item.equipmentId,
                        nid: nids ? nids[0] : '',
                      },
                      point
                    )
                  }
                }
              }
            break
        }
      })
    },
    // 设置自定义事件
    setWebrtcCostumEvent() {
      Webrtc2Sip.setGlobalCustomEvent(this.sessionEvent, this.stackEvent)
    },
    // sip栈事件 -- 来电等事件
    stackEvent(e, webrtc2Sip) {
      console.log('git stackEvent', e.type, e.description)
      if (e.type === 'i_new_call') {
        Webrtc2Sip.globalStackEvent(
          e,
          webrtc2Sip,
          this.handleAccept,
          this.handleHangup
        )
        this.webrtc2Sip = webrtc2Sip
      }
    },
    // 会话事件 -- 挂断等
    sessionEvent(e, sessionType, webrtc2Sip) {
      console.log('gis sessionEvent', e.type, e.description)
      if (e.type === 'terminating' || e.type === 'terminated') {
        if (this.webrtc2Sip === webrtc2Sip) {
          this.showWebrtcVideo = false
          this.makeCallForm = {}
          this.intercomForm = {}
          this.webrtc2Sip = null
          webrtc2Sip.sipHangUp()
          // 清除Webrtc2Sip自定义事件
          webrtc2Sip.clearCustomEvent()
          if (this.notify) this.notify.close()
        }
        Webrtc2Sip.globalSessionEvent(e, sessionType, webrtc2Sip)
      }
    },
    // 拒接或挂断
    handleHangup(webrtc2Sip, notify, type) {
      webrtc2Sip.sipHangUp()
      if (notify) notify.close()
      this.showWebrtcVideo = false
      this.makeCallForm = {}
      this.webrtc2Sip = null
      this.$store.commit('callFlagEvent', false)
    },
    // 接听
    handleAccept(webrtc2Sip, notify) {
      this.webrtc2Sip = webrtc2Sip
      console.log('gis handleAccept  callType-----------', webrtc2Sip.callType)
      if (webrtc2Sip.callType === 'audio/video') {
        // 视频
        this.showWebrtcVideo = true
        webrtc2Sip.sipAnswer('video_remote', 'video_local', 'gis')
        this.notify = null
        if (notify) notify.close()
      } else {
        webrtc2Sip.sipAnswer()
      }
      msgSuccess('接听电话')
      this.notify = notify
    },
    //  初始化化电子围栏
    initFenceData() {
      SDK.electronicfence()
        .then((res) => {
          res.forEach((item) => this.addFenceOverlay(item))
        })
        .catch((err) => {
          console.error('获取电子围栏失败', err)
        })
    },
    //广播联动 数据缓存
    handleBroadcastLink(e) {
      if (e.result == 'try') {
        let list = []
        let contacts = this.broadcastFormData.contacts
        contacts.forEach((item) => {
          const user = this.getUserInfoForBroadcast(item.userID)
          list.push(user)
        })
        let titleData = {
          isCheck: false,
          name: '广播',
          flowID: e.eventdata.flowID,
          list: list,
          titleStatus: 'try',
          fileName: '',
        }
        let topTitleArr = sessionStorage.getItem('topTitleArr')
        topTitleArr = topTitleArr == null ? [] : JSON.parse(topTitleArr)
        topTitleArr.push(titleData)
        sessionStorage.setItem('topTitleArr', JSON.stringify(topTitleArr))
      }
    },
    //构造广播联动数据
    getUserInfoForBroadcast(id) {
      let emp = deepClone(this.employeeMap.get(id))
      if (!emp) {
        let memberData = deepClone(this.operatorMap.get(id))
        if (!memberData) {
          return ''
        }
        emp = {
          defaultNo: memberData.mainTel,
          realName: memberData.realName,
          order: memberData.orderNO,
          telType: memberData.mainTelType == 'phone' ? '话机' : '手咪',
          myDutyName: '调度员',
          groupName: memberData.groupName,
          group: [{ groupID: memberData.groupID }],
          device: [
            {
              deviceTel: memberData.mainTel,
              deviceType: memberData.mainTelType,
              isCheck: true,
            },
            {
              deviceTel: memberData.viceTel,
              deviceType: memberData.viceTelType,
              isCheck: false,
            },
          ],
        }
      }
      if (!emp.myDutyName) {
        emp.myDutyName = emp.dutyName.length > 0 ? emp.dutyName[0] : '无职务'
      }
      emp.device.forEach((el, index) => {
        el.isCheck = false
        if (emp.defaultNo == el.deviceTel) {
          el.isCheck = true
        }
      })
      emp.deviceStatus = 'idle' //默认空闲
      emp.isRecord = true //默认开启录音
      const status = this.deviceStatusMap.get(emp.defaultNo)
      if (status) {
        emp.deviceStatus = status
      }
      const empdata = { ...emp }
      return empdata
    },
    // 初始化
    init() {
      SDK.setEvent(this.event)
      // 设置websocket回调函数
      this.setWebsocketCallback(this.handleMsg)

      this.initFenceData()

      // 进入页面时，缓存数据播放
      if (sessionStorage.getItem('gis_video_play_data')) {
        let playData = JSON.parse(sessionStorage.getItem('gis_video_play_data'))
        if (playData.playList && playData.playList.length > 0) {
          this.camaraBoxData = playData.camaras
          this.camaraBoxPlayingData = playData.playList
          this.showCamaraBox = true
        } else {
          this.$store.commit('GisPlayData', null)
          this.showCamaraBox = false
          this.camaraBoxData = []
        }
      } else {
        this.showCamaraBox = false
      }
      // 取到路由带过来的参数
      let routerParams = this.$route.params
      console.log('gis 取到路由带过来的参数', routerParams)
      if (routerParams.videoId) {
        let videoIds = routerParams.videoId.split(',')
        this.playBindingVideo(videoIds)
      }
    },
  },

  mounted() {
    this.initBMap()
    window['playBindingVideo'] = (res) => {
      this.playBindingVideo.call(this, res)
    }
  },
  activated() {
    this.init()
    this.setWebrtcCostumEvent()

    if (this.webrtc2Sip) {
      this.webrtc2Sip.playVideo('gis')
    }

    this.subscribeLocation()
  },
  deactivated() {
    Webrtc2Sip.setGlobalCustomEvent(null, null)
  },
  directives: {
    drag(el) {
      let dragDom = el //当前元素
      let self = this //上下文
      //禁止选择网页上的文字
      document.onselectstart = function () {
        return false
      }
      const sty = (function () {
        if (window.document.currentStyle) {
          return (dom, attr) => dom.currentStyle[attr]
        } else {
          return (dom, attr) => getComputedStyle(dom, false)[attr]
        }
      })()
      dragDom.onmousedown = function (e) {
        e.preventDefault()
        let box = document.getElementById('map-container')
        //鼠标按下，计算当前元素距离可视区的距离
        const disX = e.clientX
        const disY = e.clientY
        const dragDomWidth = dragDom.offsetWidth
        const dragDomheight = dragDom.offsetHeight
        const screenWidth = box.clientWidth
        const screenHeight = box.clientHeight
        const minDragDomLeft = dragDom.offsetLeft
        const maxDragDomLeft = screenWidth - dragDomWidth - dragDom.offsetLeft
        const minDragDomTop = dragDom.offsetTop
        const maxDragDomTop = screenHeight - dragDomheight - dragDom.offsetTop
        // 获取到的值带px 正则匹配替换
        let styL = sty(dragDom, 'left')
        let styT = sty(dragDom, 'top')
        // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
        if (styL.includes('%')) {
          styL = +screenWidth * (+styL.replace(/\%/g, '') / 100)
          styT = +screenHeight * (+styT.replace(/\%/g, '') / 100)
        } else {
          styL = +styL.replace(/\px/g, '')
          styT = +styT.replace(/\px/g, '')
        }
        document.onmousemove = function (e) {
          //通过事件委托，计算移动的距离
          let left = e.clientX - disX
          let top = e.clientY - disY
          // 边界处理
          if (-left > minDragDomLeft) {
            left = -minDragDomLeft
          } else if (left > maxDragDomLeft) {
            left = maxDragDomLeft
          }

          if (-top > minDragDomTop) {
            top = -minDragDomTop
          } else if (top > maxDragDomTop) {
            top = maxDragDomTop
          }
          // 移动当前元素
          dragDom.style.cssText += `; left: ${left + styL}px; top: ${
            top + styT
          }px;`
        }
        document.onmouseup = function (e) {
          document.onmousemove = null
          document.onmouseup = null
        }
        return false
      }
    },
  },
}
</script>

<style lang="scss" scoped>
@import '~@/assets/scss/map.scss';
</style>

<style lang="scss">
@import '~@/assets/scss/map_global.scss';
</style>
<style>
.dot {
  display: block;
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  top: 0;
  z-index: 50;
}

.dot:hover {
  z-index: 100;
}

/* 内环  */
.dot > .before {
  content: '';
  position: absolute;
  width: 20px;
  height: 20px;
  left: 5px;
  top: 15px;
  border: 2px solid #4dbbda;
  border-radius: 100%;
  background-color: #4dbbda;
  z-index: 2;
  animation: color1 1s ease-out;
  animation-iteration-count: infinite;
}

/* 产生动画（向外扩散变大）的圆圈  */
.dot > .after {
  content: '';
  position: absolute;
  width: 40px;
  height: 40px;
  top: 5px;
  left: -5px;
  border: 3px solid #4dbbda;
  -webkit-border-radius: 30px;
  -moz-border-radius: 30px;
  border-radius: 30px;
  z-index: 1;
  opacity: 0;
  animation: color2 1s ease-out;
  animation-iteration-count: infinite;
}

@keyframes color1 {
  0% {
    transform: scale(0.1);
    opacity: 0.5;
    border-color: #1da5ce;
  }

  25% {
    transform: scale(0.5);
    opacity: 0.5;
    border-color: #0b93d6;
  }

  50% {
    transform: scale(1);
    opacity: 0.5;
    border-color: #13b5e5;
  }

  75% {
    transform: scale(1);
    opacity: 0;
  }

  100% {
    transform: scale(1);
    opacity: 0;
  }
}

@keyframes color2 {
  0% {
    transform: scale(0.5);
    opacity: 0;
  }

  25% {
    transform: scale(0.5);
    opacity: 0;
  }

  49% {
    -webkit-transform: scale(0.5);
    opacity: 0;
  }

  50% {
    transform: scale(0.5);
    opacity: 0.5;
    border-color: #4b9ec7;
  }

  75% {
    transform: scale(0.8);
    opacity: 0.5;
    border-color: #4cb5d6;
  }

  100% {
    transform: scale(1);
    opacity: 0.5;
    border-color: #9cddeb;
  }
}

.dot > div {
  z-index: 3;
  top: 0;
  left: 0;
}
</style>

